title_bar.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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. self.Icons["battery"] = self.Icons["battery_unknown"]
  225. if FileExists(sysgo.Battery) == false {
  226. return
  227. }
  228. file, err := os.Open(sysgo.Battery)
  229. if err != nil {
  230. fmt.Println("Could not open file ", sysgo.Battery)
  231. return
  232. }
  233. defer file.Close()
  234. bat_uevent := make(map[string]string)
  235. scanner := bufio.NewScanner(file)
  236. scanner.Split(bufio.ScanLines)
  237. for scanner.Scan() {
  238. line := scanner.Text()
  239. line = strings.Trim(line, " ")
  240. pis := strings.Split(line, "=")
  241. if len(pis) > 1 {
  242. bat_uevent[pis[0]] = pis[1]
  243. }
  244. }
  245. cur_cap := 0
  246. if val, ok := bat_uevent["POWER_SUPPLY_CAPACITY"]; ok {
  247. cur_cap, _ = strconv.Atoi(val)
  248. } else {
  249. cur_cap = 0
  250. }
  251. cap_ge := 0
  252. for i, v := range bat_segs {
  253. if cur_cap >= v[0] && cur_cap <= v[1] {
  254. cap_ge = i
  255. break
  256. }
  257. }
  258. if val, ok := bat_uevent["POWER_SUPPLY_STATUS"]; ok {
  259. if val == "Charging" {
  260. self.Icons["battery_charging"].SetIconIndex(cap_ge)
  261. self.Icons["battery"] = self.Icons["battery_charging"]
  262. } else {
  263. self.Icons["battery_discharging"].SetIconIndex(cap_ge)
  264. self.Icons["battery"] = self.Icons["battery_discharging"]
  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_charging := NewTitleBarIconItem()
  297. battery_charging.MyType = ICON_TYPES["STAT"]
  298. battery_charging.Parent = self
  299. battery_charging.ImageName = self.icon_base_path + "withcharging.png"
  300. battery_charging.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_charging"] = battery_charging
  302. battery_discharging := NewTitleBarIconItem()
  303. battery_discharging.MyType = ICON_TYPES["STAT"]
  304. battery_discharging.Parent = self
  305. battery_discharging.ImageName = self.icon_base_path + "without_charging.png"
  306. battery_discharging.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  307. self.Icons["battery_discharging"] = battery_discharging
  308. battery_unknown := NewTitleBarIconItem()
  309. battery_unknown.MyType = ICON_TYPES["STAT"]
  310. battery_unknown.Parent = self
  311. battery_unknown.ImageName = self.icon_base_path + "battery_unknown.png"
  312. battery_unknown.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  313. self.Icons["battery_unknown"] = battery_unknown
  314. self.CheckBatteryStat()
  315. sound_volume := NewTitleBarIconItem()
  316. sound_volume.MyType = ICON_TYPES["STAT"]
  317. sound_volume.Parent = self
  318. sound_volume.ImageName = self.icon_base_path + "soundvolume.png"
  319. sound_volume.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  320. self.Icons["soundvolume"] = sound_volume
  321. self.SyncSoundVolume()
  322. bluetooth := NewTitleBarIconItem()
  323. bluetooth.MyType = ICON_TYPES["STAT"]
  324. bluetooth.Parent = self
  325. bluetooth.ImageName = self.icon_base_path + "bluetooth.png"
  326. bluetooth.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  327. self.Icons["bluetooth"] = bluetooth
  328. self.CheckBluetooth()
  329. round_corners := NewTitleBarIconItem()
  330. round_corners.IconWidth = 10
  331. round_corners.IconHeight = 10
  332. round_corners.MyType = ICON_TYPES["STAT"]
  333. round_corners.Parent = self
  334. round_corners.ImgSurf = MyIconPool.GetImgSurf("roundcorners")
  335. round_corners.Adjust(0, 0, 10, 10, 0)
  336. self.Icons["round_corners"] = round_corners
  337. dlstatus := NewTitleBarIconItem()
  338. dlstatus.MyType = ICON_TYPES["STAT"]
  339. dlstatus.Parent = self
  340. if FileExists(self.icon_base_path + "dlstatus18.png") {
  341. dlstatus.ImageName = self.icon_base_path + "dlstatus18.png"
  342. }
  343. dlstatus.Adjust(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2, self.IconWidth, self.IconHeight, 0)
  344. self.Icons["dlstatus"] = dlstatus
  345. self.UpdateDownloadStatus()
  346. if self.IsWifiConnectedNow() {
  347. print("wifi is connected")
  348. } else {
  349. cmd := "sudo rfkill list | grep yes | cut -d \" \" -f3" //make sure sudo rfkill needs no password
  350. out, err := exec.Command("bash", "-c", cmd).Output()
  351. if err != nil {
  352. fmt.Printf("Failed to execute command: %s\n", cmd)
  353. } else {
  354. outs := strings.Split(string(out), "\n")
  355. if len(outs) > 0 && outs[0] == "yes" {
  356. self.InAirPlaneMode = true
  357. } else {
  358. self.InAirPlaneMode = false
  359. }
  360. }
  361. }
  362. self.UpdateTimeLocation()
  363. }
  364. func (self *TitleBar) ClearCanvas() {
  365. surface.Fill(self.CanvasHWND, self.SkinManager.GiveColor("TitleBg"))
  366. self.Icons["round_corners"].NewCoord(5, 5)
  367. self.Icons["round_corners"].SetIconIndex(0)
  368. self.Icons["round_corners"].Draw()
  369. self.Icons["round_corners"].NewCoord(self.Width-5, 5)
  370. self.Icons["round_corners"].SetIconIndex(1)
  371. self.Icons["round_corners"].Draw()
  372. }
  373. func (self *TitleBar) UpdateTimeLocation() {
  374. d, err := ioutil.ReadFile("/etc/localtime")
  375. if err != nil {
  376. return
  377. }
  378. self.MyTimeLocation, err = gotime.LoadLocationFromTZData("local", d)
  379. if err != nil {
  380. fmt.Println(err)
  381. self.MyTimeLocation = nil
  382. }
  383. }
  384. func (self *TitleBar) GetLocalTime() gotime.Time {
  385. if self.MyTimeLocation == nil {
  386. return gotime.Now()
  387. } else {
  388. return gotime.Now().In(self.MyTimeLocation)
  389. }
  390. }
  391. func (self *TitleBar) Draw(title string) {
  392. self.ClearCanvas()
  393. self.Title = title
  394. cur_time := jodaTime.Format("HH:mm", self.GetLocalTime())
  395. time_text_w, time_text_h := font.Size(self.TimeFont, cur_time)
  396. title_text_w, title_text_h := font.Size(self.TitleFont, self.Title)
  397. title_text_surf := font.Render(self.TitleFont, self.Title, true, self.SkinManager.GiveColor("Text"), nil)
  398. 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)
  399. time_text_surf := font.Render(self.TimeFont, cur_time, true, self.SkinManager.GiveColor("Text"), nil)
  400. 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)
  401. start_x := Width - time_text_w - self.ROffset - self.IconWidth*3 // close to the time_text
  402. self.Icons["bluetooth"].NewCoord(start_x-self.IconWidth, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  403. self.Icons["sound"].NewCoord(start_x, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  404. self.Icons["battery"].NewCoord(start_x+self.IconWidth+self.IconWidth+8, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  405. if self.IsWifiConnectedNow() == true {
  406. ge := self.GetWifiStrength()
  407. //fmt.Println("wifi ge: ",ge)
  408. if ge > 0 {
  409. self.Icons["wifistatus"].SetIconIndex(ge)
  410. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  411. self.Icons["wifistatus"].Draw()
  412. } else {
  413. self.Icons["wifistatus"].SetIconIndex(0)
  414. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  415. self.Icons["wifistatus"].Draw()
  416. }
  417. } else {
  418. self.Icons["wifistatus"].SetIconIndex(0)
  419. self.Icons["wifistatus"].NewCoord(start_x+self.IconWidth+5, self.IconHeight/2+(self.BarHeight-self.IconHeight)/2)
  420. self.Icons["wifistatus"].Draw()
  421. }
  422. self.Icons["sound"].Draw()
  423. self.Icons["battery"].Draw()
  424. self.Icons["bluetooth"].Draw()
  425. draw.Line(self.CanvasHWND, self.SkinManager.GiveColor("Line"), 0, self.BarHeight, self.Width, self.BarHeight, self.BorderWidth)
  426. if self.HWND != nil {
  427. rect_ := rect.Rect(self.PosX, self.PosY, self.Width, self.Height)
  428. surface.Blit(self.HWND, self.CanvasHWND, &rect_, nil)
  429. }
  430. title_text_surf.Free()
  431. time_text_surf.Free()
  432. }