title_bar.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. package UI
  2. import (
  3. "context"
  4. "bufio"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "os/exec"
  10. "strconv"
  11. "strings"
  12. gotime "time"
  13. "github.com/veandco/go-sdl2/sdl"
  14. "github.com/veandco/go-sdl2/ttf"
  15. "github.com/zyxar/argo/rpc"
  16. "github.com/cuu/gogame/draw"
  17. "github.com/cuu/gogame/font"
  18. "github.com/cuu/gogame/rect"
  19. "github.com/cuu/gogame/surface"
  20. "github.com/itchyny/volume-go"
  21. "github.com/vjeantet/jodaTime"
  22. "github.com/clockworkpi/LauncherGoDev/sysgo"
  23. )
  24. var TitleBar_BarHeight = 24
  25. type TitleBarIconItem struct {
  26. MultiIconItem
  27. Parent *TitleBar
  28. }
  29. func NewTitleBarIconItem() *TitleBarIconItem {
  30. m := &TitleBarIconItem{}
  31. m.IconIndex = 0
  32. m.IconWidth = 18
  33. m.IconHeight = 18
  34. m.Align = ALIGN["VCenter"]
  35. return m
  36. }
  37. func (self *TitleBarIconItem) Adjust(x, y, w, h, at int) {
  38. self.PosX = x
  39. self.PosY = y
  40. self.Width = w
  41. self.Height = h
  42. self.AnimationTime = at
  43. if self.Label != nil {
  44. self.Label.SetCanvasHWND(self.Parent.CanvasHWND)
  45. }
  46. self.CreateImgSurf()
  47. // self.AdjustLinkPage()
  48. }
  49. func (self *TitleBarIconItem) Draw() {
  50. parent_x, parent_y := self.Parent.PosX, self.Parent.PosY
  51. if self.Label != nil {
  52. // lab_x,lab_y := self.Label.Coord()
  53. lab_w, lab_h := self.Label.Size()
  54. if self.Align == ALIGN["VCenter"] {
  55. self.Label.NewCoord(self.PosX-lab_w/2+parent_x, self.PosY+self.Height/2+6+parent_y)
  56. } else if self.Align == ALIGN["HLeft"] {
  57. self.Label.NewCoord(self.PosX+self.Width/2+3+parent_x, self.PosY-lab_h/2+parent_y)
  58. }
  59. self.Label.Draw()
  60. }
  61. if self.ImgSurf != nil {
  62. portion := rect.Rect(0, self.IconIndex*self.IconHeight, self.IconWidth, self.IconHeight)
  63. surface.Blit(self.Parent.CanvasHWND,
  64. self.ImgSurf, draw.MidRect(self.PosX+parent_x, self.PosY+parent_y,
  65. self.Width, self.Height, Width, Height), &portion)
  66. }
  67. }
  68. type TitleBar struct {
  69. Widget
  70. BarHeight int
  71. LOffset int
  72. ROffset int
  73. Icons map[string]IconItemInterface
  74. IconWidth int
  75. IconHeight int
  76. BorderWidth int
  77. CanvasHWND *sdl.Surface
  78. HWND *sdl.Surface
  79. Title string
  80. InLowBackLight int
  81. InAirPlaneMode bool
  82. SkinManager *SkinManager //set by MainScreen
  83. icon_base_path string /// SkinMap("gameshell/titlebar_icons/")
  84. MyTimeLocation *gotime.Location
  85. TitleFont *ttf.Font
  86. TimeFont *ttf.Font
  87. }
  88. func NewTitleBar() *TitleBar {
  89. t := &TitleBar{}
  90. t.BorderWidth = 1
  91. t.BarHeight = TitleBar_BarHeight
  92. t.Height = t.BarHeight + t.BorderWidth
  93. t.Width = Width
  94. t.IconWidth = 18
  95. t.IconHeight = 18
  96. t.LOffset = 3
  97. t.ROffset = 3
  98. t.Icons = make(map[string]IconItemInterface)
  99. t.icon_base_path = SkinMap("sysgo/gameshell/titlebar_icons/")
  100. t.TitleFont = Fonts["varela16"]
  101. t.TimeFont = Fonts["varela12"]
  102. t.InLowBackLight = -1
  103. return t
  104. }
  105. func (self *TitleBar) Redraw() {
  106. self.UpdateDownloadStatus()
  107. SwapAndShow()
  108. }
  109. func (self *TitleBar) UpdateDownloadStatus() {
  110. rpcc, err := rpc.New(context.Background(), sysgo.Aria2Url, "", gotime.Second, nil)
  111. if err != nil {
  112. fmt.Fprintln(os.Stderr, err)
  113. return
  114. }
  115. if resp,err := rpcc.GetGlobalStat();err == nil {
  116. num_active,_ := strconv.Atoi(resp.NumActive)
  117. if num_active > 0 {
  118. self.Icons["dlstatus"].SetIconIndex(1)
  119. }else if num_active == 0 {
  120. self.Icons["dlstatus"].SetIconIndex(0)
  121. }
  122. }
  123. defer rpcc.Close()
  124. }
  125. func (self *TitleBar) RoundRobinCheck() {
  126. for {
  127. if self.InLowBackLight < 0 {
  128. self.CheckBatteryStat()
  129. self.CheckBluetooth()
  130. self.UpdateWifiStrength()
  131. self.UpdateDownloadStatus()
  132. SwapAndShow()
  133. } else if self.InLowBackLight >= 0 {
  134. self.InLowBackLight += 1
  135. if self.InLowBackLight > 10 {
  136. self.CheckBatteryStat()
  137. self.CheckBluetooth()
  138. self.UpdateWifiStrength()
  139. self.UpdateDownloadStatus()
  140. self.InLowBackLight = 0 // reset
  141. }
  142. }
  143. gotime.Sleep(3000 * gotime.Millisecond)
  144. }
  145. }
  146. func (self *TitleBar) IsWifiConnectedNow() bool {
  147. cli := fmt.Sprintf("ip -4 addr show %s | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'", sysgo.WifiDev)
  148. out := System(cli)
  149. if len(out) > 7 {
  150. if strings.Contains(out, "not") {
  151. return false
  152. } else {
  153. return true
  154. }
  155. }
  156. return false
  157. }
  158. func (self *TitleBar) UpdateWifiStrength() {
  159. self.Draw(self.Title)
  160. }
  161. func (t *TitleBar) GetWifiStrength() int {
  162. qua := 0
  163. cli := fmt.Sprintf("sudo iwconfig %s | grep Signal | /usr/bin/awk '{print $4}' | /usr/bin/cut -d'=' -f2", sysgo.WifiDev)
  164. out := System(cli)
  165. if len(out) > 2 {
  166. if strings.Contains(out, "No") == false {
  167. out = strings.TrimSuffix(out, "\n")
  168. stren, err := strconv.ParseInt(out, 10, 0)
  169. if err == nil {
  170. qua = 2 * (int(stren) + 100)
  171. } else {
  172. fmt.Println(err)
  173. }
  174. }
  175. }
  176. segs := [][]int{[]int{-2, -1}, []int{0, 25}, []int{25, 50}, []int{50, 75}, []int{75, 100}}
  177. stren_number := qua
  178. ge := 0
  179. if stren_number == 0 {
  180. return ge
  181. }
  182. for i, v := range segs {
  183. if stren_number >= v[0] && stren_number <= v[1] {
  184. ge = i
  185. break
  186. }
  187. }
  188. return ge
  189. }
  190. func (self *TitleBar) SyncSoundVolume() {
  191. vol, err := volume.GetVolume()
  192. if err != nil {
  193. log.Printf("TitleBar SyncSoundVolume get volume failed: %+v\n", err)
  194. vol = 0
  195. }
  196. fmt.Printf("TitleBar SyncSoundVolume current volume: %d\n", vol)
  197. snd_segs := [][]int{[]int{0, 10}, []int{10, 30}, []int{30, 70}, []int{70, 100}}
  198. ge := 0
  199. for i, v := range snd_segs {
  200. if vol >= v[0] && vol <= v[1] {
  201. ge = i
  202. break
  203. }
  204. }
  205. self.Icons["soundvolume"].SetIconIndex(ge)
  206. self.Icons["sound"] = self.Icons["soundvolume"]
  207. //
  208. }
  209. // for outside widget to update sound icon
  210. func (self *TitleBar) SetSoundVolume(vol int) {
  211. snd_segs := [][]int{[]int{0, 10}, []int{10, 30}, []int{30, 70}, []int{70, 100}}
  212. ge := 0
  213. for i, v := range snd_segs {
  214. if vol >= v[0] && vol <= v[1] {
  215. ge = i
  216. break
  217. }
  218. }
  219. self.Icons["soundvolume"].SetIconIndex(ge)
  220. self.Icons["sound"] = self.Icons["soundvolume"]
  221. }
  222. func (self *TitleBar) CheckBatteryStat() {
  223. bat_segs := [][]int{[]int{0, 6}, []int{7, 15}, []int{16, 20}, []int{21, 30}, []int{31, 50}, []int{51, 60}, []int{61, 80}, []int{81, 90}, []int{91, 100}}
  224. if FileExists(sysgo.Battery) == false {
  225. return
  226. }
  227. file, err := os.Open(sysgo.Battery)
  228. if err != nil {
  229. fmt.Println("Could not open file ", sysgo.Battery)
  230. return
  231. }
  232. defer file.Close()
  233. bat_uevent := make(map[string]string)
  234. scanner := bufio.NewScanner(file)
  235. scanner.Split(bufio.ScanLines)
  236. for scanner.Scan() {
  237. line := scanner.Text()
  238. line = strings.Trim(line, " ")
  239. pis := strings.Split(line, "=")
  240. if len(pis) > 1 {
  241. bat_uevent[pis[0]] = pis[1]
  242. }
  243. }
  244. cur_cap := 0
  245. if val, ok := bat_uevent["POWER_SUPPLY_CAPACITY"]; ok {
  246. cur_cap, _ = strconv.Atoi(val)
  247. } else {
  248. cur_cap = 0
  249. }
  250. cap_ge := 0
  251. for i, v := range bat_segs {
  252. if cur_cap >= v[0] && cur_cap <= v[1] {
  253. cap_ge = i
  254. break
  255. }
  256. }
  257. if val, ok := bat_uevent["POWER_SUPPLY_STATUS"]; ok {
  258. if val == "Charging" {
  259. self.Icons["battery"].SetIconIndex(1+cap_ge)
  260. } else {
  261. self.Icons["battery"].SetIconIndex(1+9+cap_ge)
  262. }
  263. }
  264. }
  265. func (self *TitleBar) SetBatteryStat(bat int) {
  266. }
  267. func (self *TitleBar) CheckBluetooth() {
  268. out := System("hcitool dev | grep hci0 |cut -f3")
  269. if len(out) < 17 {
  270. fmt.Println("Titlebar CheckBluetooth: no bluetooth", out)
  271. self.Icons["bluetooth"].SetIconIndex(2)
  272. return
  273. } else {
  274. out = System("sudo rfkill list | grep hci0 -A 2 | grep yes")
  275. if len(out) > 10 {
  276. self.Icons["bluetooth"].SetIconIndex(1)
  277. return
  278. }
  279. }
  280. self.Icons["bluetooth"].SetIconIndex(0)
  281. }
  282. func (self *TitleBar) Init(main_screen *MainScreen) {
  283. start_x := 0
  284. self.CanvasHWND = surface.Surface(self.Width, self.Height)
  285. self.HWND = main_screen.HWND
  286. self.SkinManager = main_screen.SkinManager
  287. icon_wifi_status := NewTitleBarIconItem()
  288. icon_wifi_status.MyType = ICON_TYPES["STAT"]
  289. icon_wifi_status.ImageName = self.icon_base_path + "wifi.png"
  290. icon_wifi_status.Parent = self
  291. icon_wifi_status.Adjust(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  292. self.Icons["wifistatus"] = icon_wifi_status
  293. battery_unknown := NewTitleBarIconItem()
  294. battery_unknown.MyType = ICON_TYPES["STAT"]
  295. battery_unknown.Parent = self
  296. battery_unknown.ImageName = self.icon_base_path + "battery.png"
  297. battery_unknown.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  298. self.Icons["battery"] = battery_unknown
  299. self.CheckBatteryStat()
  300. sound_volume := NewTitleBarIconItem()
  301. sound_volume.MyType = ICON_TYPES["STAT"]
  302. sound_volume.Parent = self
  303. sound_volume.ImageName = self.icon_base_path + "soundvolume.png"
  304. sound_volume.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  305. self.Icons["soundvolume"] = sound_volume
  306. self.SyncSoundVolume()
  307. bluetooth := NewTitleBarIconItem()
  308. bluetooth.MyType = ICON_TYPES["STAT"]
  309. bluetooth.Parent = self
  310. bluetooth.ImageName = self.icon_base_path + "bluetooth.png"
  311. bluetooth.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  312. self.Icons["bluetooth"] = bluetooth
  313. self.CheckBluetooth()
  314. round_corners := NewTitleBarIconItem()
  315. round_corners.IconWidth = 10
  316. round_corners.IconHeight = 10
  317. round_corners.MyType = ICON_TYPES["STAT"]
  318. round_corners.Parent = self
  319. round_corners.ImgSurf = MyIconPool.GetImgSurf("roundcorners")
  320. round_corners.Adjust(0, 0, 10, 10, 0)
  321. self.Icons["round_corners"] = round_corners
  322. dlstatus := NewTitleBarIconItem()
  323. dlstatus.MyType = ICON_TYPES["STAT"]
  324. dlstatus.Parent = self
  325. if FileExists(self.icon_base_path + "dlstatus18.png") {
  326. dlstatus.ImageName = self.icon_base_path + "dlstatus18.png"
  327. }
  328. dlstatus.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  329. self.Icons["dlstatus"] = dlstatus
  330. self.UpdateDownloadStatus()
  331. if self.IsWifiConnectedNow() {
  332. print("wifi is connected")
  333. } else {
  334. cmd := "sudo rfkill list | grep yes | cut -d \" \" -f3" //make sure sudo rfkill needs no password
  335. out, err := exec.Command("bash", "-c", cmd).Output()
  336. if err != nil {
  337. fmt.Printf("Failed to execute command: %s\n", cmd)
  338. } else {
  339. outs := strings.Split(string(out), "\n")
  340. if len(outs) > 0 && outs[0] == "yes" {
  341. self.InAirPlaneMode = true
  342. } else {
  343. self.InAirPlaneMode = false
  344. }
  345. }
  346. }
  347. self.UpdateTimeLocation()
  348. }
  349. func (self *TitleBar) ClearCanvas() {
  350. surface.Fill(self.CanvasHWND, self.SkinManager.GiveColor("TitleBg"))
  351. self.Icons["round_corners"].NewCoord(5, 5)
  352. self.Icons["round_corners"].SetIconIndex(0)
  353. self.Icons["round_corners"].Draw()
  354. self.Icons["round_corners"].NewCoord(self.Width-5, 5)
  355. self.Icons["round_corners"].SetIconIndex(1)
  356. self.Icons["round_corners"].Draw()
  357. }
  358. func (self *TitleBar) UpdateTimeLocation() {
  359. d, err := ioutil.ReadFile("/etc/localtime")
  360. if err != nil {
  361. return
  362. }
  363. self.MyTimeLocation, err = gotime.LoadLocationFromTZData("local", d)
  364. if err != nil {
  365. fmt.Println(err)
  366. self.MyTimeLocation = nil
  367. }
  368. }
  369. func (self *TitleBar) GetLocalTime() gotime.Time {
  370. if self.MyTimeLocation == nil {
  371. return gotime.Now()
  372. } else {
  373. return gotime.Now().In(self.MyTimeLocation)
  374. }
  375. }
  376. func (self *TitleBar) Draw(title string) {
  377. self.ClearCanvas()
  378. self.Title = title
  379. cur_time := jodaTime.Format("HH:mm", self.GetLocalTime())
  380. time_text_w, time_text_h := font.Size(self.TimeFont, cur_time)
  381. title_text_w, title_text_h := font.Size(self.TitleFont, self.Title)
  382. title_text_surf := font.Render(self.TitleFont, self.Title, true, self.SkinManager.GiveColor("Text"), nil)
  383. surface.Blit(self.CanvasHWND, title_text_surf, draw.MidRect(title_text_w/2+self.LOffset, title_text_h/2+(self.BarHeight-title_text_h)/2, title_text_w, title_text_h, Width, Height), nil)
  384. time_text_surf := font.Render(self.TimeFont, cur_time, true, self.SkinManager.GiveColor("Text"), nil)
  385. surface.Blit(self.CanvasHWND, time_text_surf, draw.MidRect(Width-time_text_w/2-self.ROffset, time_text_h/2+(self.BarHeight-time_text_h)/2, time_text_w, time_text_h, Width, Height), nil)
  386. start_x := Width - time_text_w - self.ROffset - self.IconWidth*3 // close to the time_text
  387. self.Icons["bluetooth"].NewCoord(start_x-self.IconWidth, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  388. self.Icons["sound"].NewCoord(start_x, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  389. self.Icons["battery"].NewCoord(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  390. if self.IsWifiConnectedNow() == true {
  391. ge := self.GetWifiStrength()
  392. //fmt.Println("wifi ge: ",ge)
  393. if ge > 0 {
  394. self.Icons["wifistatus"].SetIconIndex(ge)
  395. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  396. self.Icons["wifistatus"].Draw()
  397. } else {
  398. self.Icons["wifistatus"].SetIconIndex(0)
  399. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  400. self.Icons["wifistatus"].Draw()
  401. }
  402. } else {
  403. self.Icons["wifistatus"].SetIconIndex(0)
  404. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  405. self.Icons["wifistatus"].Draw()
  406. }
  407. self.Icons["sound"].Draw()
  408. self.Icons["battery"].Draw()
  409. self.Icons["bluetooth"].Draw()
  410. draw.Line(self.CanvasHWND, self.SkinManager.GiveColor("Line"), 0, self.BarHeight, self.Width, self.BarHeight, self.BorderWidth)
  411. if self.HWND != nil {
  412. rect_ := rect.Rect(self.PosX, self.PosY, self.Width, self.Height)
  413. surface.Blit(self.HWND, self.CanvasHWND, &rect_, nil)
  414. }
  415. title_text_surf.Free()
  416. time_text_surf.Free()
  417. }