title_bar.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  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. updateScreen chan bool
  88. }
  89. func NewTitleBar() *TitleBar {
  90. t := &TitleBar{}
  91. t.BorderWidth = 1
  92. t.BarHeight = TitleBar_BarHeight
  93. t.Height = t.BarHeight + t.BorderWidth
  94. t.Width = Width
  95. t.IconWidth = 18
  96. t.IconHeight = 18
  97. t.LOffset = 3
  98. t.ROffset = 3
  99. t.Icons = make(map[string]IconItemInterface)
  100. t.icon_base_path = SkinMap("sysgo/gameshell/titlebar_icons/")
  101. t.TitleFont = Fonts["varela16"]
  102. t.TimeFont = Fonts["varela12"]
  103. t.InLowBackLight = -1
  104. t.updateScreen = make(chan bool,1)
  105. return t
  106. }
  107. func (self *TitleBar) Redraw() {
  108. self.UpdateDownloadStatus()
  109. DisplayFlip()
  110. }
  111. func (self *TitleBar) UpdateDownloadStatus() {
  112. rpcc, err := rpc.New(context.Background(), sysgo.Aria2Url, "", gotime.Second, nil)
  113. if err != nil {
  114. fmt.Fprintln(os.Stderr, err)
  115. return
  116. }
  117. if resp,err := rpcc.GetGlobalStat();err == nil {
  118. num_active,_ := strconv.Atoi(resp.NumActive)
  119. if num_active > 0 {
  120. self.Icons["dlstatus"].SetIconIndex(1)
  121. }else if num_active == 0 {
  122. self.Icons["dlstatus"].SetIconIndex(0)
  123. }
  124. }
  125. defer rpcc.Close()
  126. }
  127. func (self *TitleBar) RoundRobinCheck() {
  128. for {
  129. if self.InLowBackLight < 0 {
  130. self.CheckBatteryStat()
  131. self.CheckBluetooth()
  132. self.UpdateWifiStrength()
  133. self.UpdateDownloadStatus()
  134. self.Refresh()
  135. } else if self.InLowBackLight >= 0 {
  136. self.InLowBackLight += 1
  137. if self.InLowBackLight > 10 {
  138. self.CheckBatteryStat()
  139. self.CheckBluetooth()
  140. self.UpdateWifiStrength()
  141. self.UpdateDownloadStatus()
  142. self.InLowBackLight = 0 // reset
  143. self.Refresh()
  144. }
  145. }
  146. gotime.Sleep(3000 * gotime.Millisecond)
  147. }
  148. }
  149. func (self *TitleBar) IsWifiConnectedNow() bool {
  150. cli := fmt.Sprintf("ip -4 addr show %s | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'", sysgo.WifiDev)
  151. out := System(cli)
  152. if len(out) > 7 {
  153. if strings.Contains(out, "not") {
  154. return false
  155. } else {
  156. return true
  157. }
  158. }
  159. return false
  160. }
  161. func (self *TitleBar) UpdateWifiStrength() {
  162. self.Draw(self.Title)
  163. }
  164. func (t *TitleBar) GetWifiStrength() int {
  165. qua := 0
  166. cli := fmt.Sprintf("sudo iwconfig %s | grep Signal | /usr/bin/awk '{print $4}' | /usr/bin/cut -d'=' -f2", sysgo.WifiDev)
  167. out := System(cli)
  168. if len(out) > 2 {
  169. if strings.Contains(out, "No") == false {
  170. out = strings.TrimSuffix(out, "\n")
  171. stren, err := strconv.ParseInt(out, 10, 0)
  172. if err == nil {
  173. qua = 2 * (int(stren) + 100)
  174. } else {
  175. fmt.Println(err)
  176. }
  177. }
  178. }
  179. segs := [][]int{[]int{-2, -1}, []int{0, 25}, []int{25, 50}, []int{50, 75}, []int{75, 100}}
  180. stren_number := qua
  181. ge := 0
  182. if stren_number == 0 {
  183. return ge
  184. }
  185. for i, v := range segs {
  186. if stren_number >= v[0] && stren_number <= v[1] {
  187. ge = i
  188. break
  189. }
  190. }
  191. return ge
  192. }
  193. func (self *TitleBar) SyncSoundVolume() {
  194. vol, err := volume.GetVolume()
  195. if err != nil {
  196. log.Printf("TitleBar SyncSoundVolume get volume failed: %+v\n", err)
  197. vol = 0
  198. }
  199. fmt.Printf("TitleBar SyncSoundVolume current volume: %d\n", vol)
  200. snd_segs := [][]int{[]int{0, 10}, []int{10, 30}, []int{30, 70}, []int{70, 100}}
  201. ge := 0
  202. for i, v := range snd_segs {
  203. if vol >= v[0] && vol <= v[1] {
  204. ge = i
  205. break
  206. }
  207. }
  208. self.Icons["soundvolume"].SetIconIndex(ge)
  209. self.Icons["sound"] = self.Icons["soundvolume"]
  210. //
  211. }
  212. // for outside widget to update sound icon
  213. func (self *TitleBar) SetSoundVolume(vol int) {
  214. snd_segs := [][]int{[]int{0, 10}, []int{10, 30}, []int{30, 70}, []int{70, 100}}
  215. ge := 0
  216. for i, v := range snd_segs {
  217. if vol >= v[0] && vol <= v[1] {
  218. ge = i
  219. break
  220. }
  221. }
  222. self.Icons["soundvolume"].SetIconIndex(ge)
  223. self.Icons["sound"] = self.Icons["soundvolume"]
  224. }
  225. func (self *TitleBar) CheckBatteryStat() {
  226. 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}}
  227. if FileExists(sysgo.Battery) == false {
  228. return
  229. }
  230. file, err := os.Open(sysgo.Battery)
  231. if err != nil {
  232. fmt.Println("Could not open file ", sysgo.Battery)
  233. return
  234. }
  235. defer file.Close()
  236. bat_uevent := make(map[string]string)
  237. scanner := bufio.NewScanner(file)
  238. scanner.Split(bufio.ScanLines)
  239. for scanner.Scan() {
  240. line := scanner.Text()
  241. line = strings.Trim(line, " ")
  242. pis := strings.Split(line, "=")
  243. if len(pis) > 1 {
  244. bat_uevent[pis[0]] = pis[1]
  245. }
  246. }
  247. cur_cap := 0
  248. if val, ok := bat_uevent["POWER_SUPPLY_CAPACITY"]; ok {
  249. cur_cap, _ = strconv.Atoi(val)
  250. } else {
  251. cur_cap = 0
  252. }
  253. cap_ge := 0
  254. for i, v := range bat_segs {
  255. if cur_cap >= v[0] && cur_cap <= v[1] {
  256. cap_ge = i
  257. break
  258. }
  259. }
  260. if val, ok := bat_uevent["POWER_SUPPLY_STATUS"]; ok {
  261. if val == "Charging" {
  262. self.Icons["battery"].SetIconIndex(1+cap_ge)
  263. } else {
  264. self.Icons["battery"].SetIconIndex(1+9+cap_ge)
  265. }
  266. }
  267. }
  268. func (self *TitleBar) SetBatteryStat(bat int) {
  269. }
  270. func (self *TitleBar) CheckBluetooth() {
  271. out := System("hcitool dev | grep hci0 |cut -f3")
  272. if len(out) < 17 {
  273. fmt.Println("Titlebar CheckBluetooth: no bluetooth", out)
  274. self.Icons["bluetooth"].SetIconIndex(2)
  275. return
  276. } else {
  277. out = System("sudo rfkill list | grep hci0 -A 2 | grep yes")
  278. if len(out) > 10 {
  279. self.Icons["bluetooth"].SetIconIndex(1)
  280. return
  281. }
  282. }
  283. self.Icons["bluetooth"].SetIconIndex(0)
  284. }
  285. func (self *TitleBar) Init(main_screen *MainScreen) {
  286. start_x := 0
  287. self.CanvasHWND = surface.Surface(self.Width, self.Height)
  288. self.HWND = main_screen.HWND
  289. self.SkinManager = main_screen.SkinManager
  290. icon_wifi_status := NewTitleBarIconItem()
  291. icon_wifi_status.MyType = ICON_TYPES["STAT"]
  292. icon_wifi_status.ImageName = self.icon_base_path + "wifi.png"
  293. icon_wifi_status.Parent = self
  294. icon_wifi_status.Adjust(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  295. self.Icons["wifistatus"] = icon_wifi_status
  296. battery_unknown := NewTitleBarIconItem()
  297. battery_unknown.MyType = ICON_TYPES["STAT"]
  298. battery_unknown.Parent = self
  299. battery_unknown.ImageName = self.icon_base_path + "battery.png"
  300. battery_unknown.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  301. self.Icons["battery"] = battery_unknown
  302. self.CheckBatteryStat()
  303. sound_volume := NewTitleBarIconItem()
  304. sound_volume.MyType = ICON_TYPES["STAT"]
  305. sound_volume.Parent = self
  306. sound_volume.ImageName = self.icon_base_path + "soundvolume.png"
  307. sound_volume.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  308. self.Icons["soundvolume"] = sound_volume
  309. self.SyncSoundVolume()
  310. bluetooth := NewTitleBarIconItem()
  311. bluetooth.MyType = ICON_TYPES["STAT"]
  312. bluetooth.Parent = self
  313. bluetooth.ImageName = self.icon_base_path + "bluetooth.png"
  314. bluetooth.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  315. self.Icons["bluetooth"] = bluetooth
  316. self.CheckBluetooth()
  317. round_corners := NewTitleBarIconItem()
  318. round_corners.IconWidth = 10
  319. round_corners.IconHeight = 10
  320. round_corners.MyType = ICON_TYPES["STAT"]
  321. round_corners.Parent = self
  322. round_corners.ImgSurf = MyIconPool.GetImgSurf("roundcorners")
  323. round_corners.Adjust(0, 0, 10, 10, 0)
  324. self.Icons["round_corners"] = round_corners
  325. dlstatus := NewTitleBarIconItem()
  326. dlstatus.MyType = ICON_TYPES["STAT"]
  327. dlstatus.Parent = self
  328. if FileExists(self.icon_base_path + "dlstatus18.png") {
  329. dlstatus.ImageName = self.icon_base_path + "dlstatus18.png"
  330. }
  331. dlstatus.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  332. self.Icons["dlstatus"] = dlstatus
  333. self.UpdateDownloadStatus()
  334. if self.IsWifiConnectedNow() {
  335. print("wifi is connected")
  336. } else {
  337. cmd := "sudo rfkill list | grep yes | cut -d \" \" -f3" //make sure sudo rfkill needs no password
  338. out, err := exec.Command("bash", "-c", cmd).Output()
  339. if err != nil {
  340. fmt.Printf("Failed to execute command: %s\n", cmd)
  341. } else {
  342. outs := strings.Split(string(out), "\n")
  343. if len(outs) > 0 && outs[0] == "yes" {
  344. self.InAirPlaneMode = true
  345. } else {
  346. self.InAirPlaneMode = false
  347. }
  348. }
  349. }
  350. self.UpdateTimeLocation()
  351. }
  352. func (self *TitleBar) ClearCanvas() {
  353. surface.Fill(self.CanvasHWND, self.SkinManager.GiveColor("TitleBg"))
  354. self.Icons["round_corners"].NewCoord(5, 5)
  355. self.Icons["round_corners"].SetIconIndex(0)
  356. self.Icons["round_corners"].Draw()
  357. self.Icons["round_corners"].NewCoord(self.Width-5, 5)
  358. self.Icons["round_corners"].SetIconIndex(1)
  359. self.Icons["round_corners"].Draw()
  360. go func() {
  361. sdl.Do(func() {
  362. self.RefreshLoop()
  363. })
  364. }()
  365. }
  366. func (self *TitleBar) UpdateTimeLocation() {
  367. d, err := ioutil.ReadFile("/etc/localtime")
  368. if err != nil {
  369. return
  370. }
  371. self.MyTimeLocation, err = gotime.LoadLocationFromTZData("local", d)
  372. if err != nil {
  373. fmt.Println(err)
  374. self.MyTimeLocation = nil
  375. }
  376. }
  377. func (self *TitleBar) GetLocalTime() gotime.Time {
  378. if self.MyTimeLocation == nil {
  379. return gotime.Now()
  380. } else {
  381. return gotime.Now().In(self.MyTimeLocation)
  382. }
  383. }
  384. func (self *TitleBar) Draw(title string) {
  385. self.ClearCanvas()
  386. self.Title = title
  387. cur_time := jodaTime.Format("HH:mm", self.GetLocalTime())
  388. time_text_w, time_text_h := font.Size(self.TimeFont, cur_time)
  389. title_text_w, title_text_h := font.Size(self.TitleFont, self.Title)
  390. title_text_surf := font.Render(self.TitleFont, self.Title, true, self.SkinManager.GiveColor("Text"), nil)
  391. 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)
  392. time_text_surf := font.Render(self.TimeFont, cur_time, true, self.SkinManager.GiveColor("Text"), nil)
  393. 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)
  394. start_x := Width - time_text_w - self.ROffset - self.IconWidth*3 // close to the time_text
  395. self.Icons["bluetooth"].NewCoord(start_x-self.IconWidth, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  396. self.Icons["sound"].NewCoord(start_x, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  397. self.Icons["battery"].NewCoord(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  398. if self.IsWifiConnectedNow() == true {
  399. ge := self.GetWifiStrength()
  400. //fmt.Println("wifi ge: ",ge)
  401. if ge > 0 {
  402. self.Icons["wifistatus"].SetIconIndex(ge)
  403. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  404. self.Icons["wifistatus"].Draw()
  405. } else {
  406. self.Icons["wifistatus"].SetIconIndex(0)
  407. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  408. self.Icons["wifistatus"].Draw()
  409. }
  410. } else {
  411. self.Icons["wifistatus"].SetIconIndex(0)
  412. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  413. self.Icons["wifistatus"].Draw()
  414. }
  415. self.Icons["sound"].Draw()
  416. self.Icons["battery"].Draw()
  417. self.Icons["bluetooth"].Draw()
  418. draw.Line(self.CanvasHWND, self.SkinManager.GiveColor("Line"), 0, self.BarHeight, self.Width, self.BarHeight, self.BorderWidth)
  419. if self.HWND != nil {
  420. rect_ := rect.Rect(self.PosX, self.PosY, self.Width, self.Height)
  421. surface.Blit(self.HWND, self.CanvasHWND, &rect_, nil)
  422. }
  423. title_text_surf.Free()
  424. time_text_surf.Free()
  425. }
  426. func (self *TitleBar) Refresh() {
  427. self.updateScreen <- true
  428. }
  429. func (self *TitleBar) RefreshLoop() {
  430. L:
  431. for {
  432. select {
  433. case v:= <- self.updateScreen:
  434. if v == true {
  435. DisplayFlip()
  436. }
  437. if v== false {
  438. break L
  439. }
  440. }
  441. }
  442. }