package Bluetooth import ( "fmt" "log" "os" "strings" //"errors" "github.com/fatih/structs" gotime "time" "github.com/veandco/go-sdl2/ttf" "github.com/cuu/gogame/color" "github.com/cuu/gogame/draw" "github.com/cuu/gogame/event" "github.com/cuu/gogame/font" "github.com/cuu/gogame/rect" "github.com/cuu/gogame/surface" "github.com/cuu/gogame/time" //"github.com/godbus/dbus" bleapi "github.com/muka/go-bluetooth/api" //"github.com/muka/go-bluetooth/bluez" // "github.com/muka/go-bluetooth/bluez/profile" "github.com/muka/go-bluetooth/bluez/profile/adapter" "github.com/muka/go-bluetooth/bluez/profile/device" "github.com/clockworkpi/LauncherGoDev/sysgo/UI" logrus "github.com/sirupsen/logrus" ) func showDeviceInfo(dev *device.Device1) { if dev == nil { return } props, err := dev.GetProperties() if err != nil { fmt.Printf("%s: Failed to get properties: %s\n", dev.Path, err.Error()) return } fmt.Printf("name=%s addr=%s rssi=%d\n", props.Name, props.Address, props.RSSI) } type BleForgetConfirmPage struct { UI.ConfirmPage } func NewBleForgetConfirmPage() *BleForgetConfirmPage { p := &BleForgetConfirmPage{} p.ListFont = UI.Fonts["veramono20"] p.FootMsg = [5]string{"Nav", "", "", "Cancel", "Yes"} p.ConfirmText = "Confirm Forget?" p.ConfirmPage.ConfirmText = p.ConfirmText return p } func (self *BleForgetConfirmPage) KeyDown(ev *event.Event) { if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] { self.ReturnToUpLevelPage() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["B"] { self.SnapMsg("Deleting") self.Screen.Refresh() time.BlockDelay(400) self.ReturnToUpLevelPage() self.Screen.Refresh() } } func (self *BleForgetConfirmPage) Draw() { self.DrawBG() for _, v := range self.MyList { v.Draw() } } type BleInfoPageSelector struct { UI.InfoPageSelector } func NewBleInfoPageSelector() *BleInfoPageSelector { p := &BleInfoPageSelector{} p.Width = UI.Width p.BackgroundColor = &color.Color{131, 199, 219, 255} //SkinManager().GiveColor('Front') return p } func (self *BleInfoPageSelector) Draw() { idx := self.Parent.GetPsIndex() mylist := self.Parent.GetMyList() if idx < len(mylist) { _, y := mylist[idx].Coord() _, h := mylist[idx].Size() x := self.PosX + 2 self.PosY = y + 1 self.Height = h - 3 canvas_ := self.Parent.GetCanvasHWND() rect_ := rect.Rect(x, self.PosY, self.Width-4, self.Height) draw.AARoundRect(canvas_, &rect_, self.BackgroundColor, 4, 0, self.BackgroundColor) } } type BleInfoPage struct { UI.Page ListFontObj *ttf.Font ListSmFontObj *ttf.Font ListSm2FontObj *ttf.Font AList map[string]interface{} Scroller *UI.ListScroller ConfirmPage1 *BleForgetConfirmPage MyDevice *device.Device1 // from NetItem-> from BluetoothPage Props *device.Device1Properties Path string } func NewBleInfoPage() *BleInfoPage { p := &BleInfoPage{} p.FootMsg = [5]string{"Nav", "Disconnect", "Forget", "Back", ""} p.ListFontObj = UI.Fonts["varela15"] p.ListSmFontObj = UI.Fonts["varela12"] p.ListSm2FontObj = UI.Fonts["varela11"] return p } func (self *BleInfoPage) Init() { if self.Screen != nil { if self.Screen.CanvasHWND != nil && self.CanvasHWND == nil { self.CanvasHWND = self.Screen.CanvasHWND } } self.PosX = self.Index * self.Screen.Width self.Width = self.Screen.Width // equals to screen width self.Height = self.Screen.Height ps := NewBleInfoPageSelector() ps.Parent = self self.Ps = ps self.PsIndex = 0 self.Scroller = UI.NewListScroller() self.Scroller.Parent = self self.Scroller.PosX = 2 self.Scroller.PosY = 2 self.Scroller.Init() self.ConfirmPage1 = NewBleForgetConfirmPage() self.ConfirmPage1.Screen = self.Screen self.ConfirmPage1.Name = "Confirm Forget" self.ConfirmPage1.Init() } func (self *BleInfoPage) GenList() { self.AList = structs.Map(self.Props) //map[string]interface{} self.MyList = nil self.PsIndex = 0 start_x := 0 start_y := 0 i := 0 skip_arrays := []string{"ManufacturerData", "AdvertisingFlags", "ServiceData"} for k, v := range self.AList { skip2 := false for _, u := range skip_arrays { if strings.HasPrefix(k, u) { skip2 = true break } } if skip2 { continue } li := UI.NewInfoPageListItem() li.Parent = self li.PosX = start_x li.PosY = start_y + i*NetItemDefaultHeight li.Width = UI.Width li.Fonts["normal"] = self.ListFontObj if k == "UUIDs" { li.Fonts["small"] = self.ListSm2FontObj } else { li.Fonts["small"] = self.ListSmFontObj } li.Init(k) li.Flag = k sm_text := "" if k == "UUIDs" { if len(v.([]string)) > 1 { sm_text = v.([]string)[0] } else { sm_text = "" } } else { sm_text = fmt.Sprintf("%v", v) } if len(sm_text) > 20 { sm_text = sm_text[:20] } li.SetSmallText(sm_text) li.PosX = 2 self.MyList = append(self.MyList, li) i += 1 } } func (self *BleInfoPage) ScrollUp() { if len(self.MyList) == 0 { return } self.PsIndex -= 1 if self.PsIndex < 0 { self.PsIndex = 0 } cur_li := self.MyList[self.PsIndex] x, y := cur_li.Coord() if y < 0 { for i, v := range self.MyList { x, y = v.Coord() _, h := v.Size() self.MyList[i].NewCoord(x, y+h) } } } func (self *BleInfoPage) ScrollDown() { if len(self.MyList) == 0 { return } self.PsIndex += 1 if self.PsIndex >= len(self.MyList) { self.PsIndex = len(self.MyList) - 1 } cur_li := self.MyList[self.PsIndex] x, y := cur_li.Coord() _, h := cur_li.Size() if y+h > self.Height { for i, v := range self.MyList { x, y = v.Coord() _, h = v.Size() self.MyList[i].NewCoord(x, y-h) } } } func (self *BleInfoPage) TryToForget() { //muka Adapter1 RemoveDevice Path adapter, err := bleapi.GetAdapter(adapterID) if err == nil { self.Screen.MsgBox.SetText("Forgeting") self.Screen.MsgBox.Draw() self.Screen.SwapAndShow() err = adapter.RemoveDevice(self.MyDevice.Path()) if err != nil { fmt.Println("BleInfoPage TryToForget: ", err) } time.BlockDelay(400) self.ReturnToUpLevelPage() self.Screen.Refresh() } else { fmt.Println("BleInfoPage TryToForget GetAdapter: ", err) } } func (self *BleInfoPage) TryToDisconnect() { is_connected, _ := self.MyDevice.GetConnected() if is_connected { self.Screen.FootBar.UpdateNavText("Disconnecting") self.Screen.ShowMsg("Disconnecting",0) self.MyDevice.Disconnect() time.BlockDelay(350) self.ReturnToUpLevelPage() self.Screen.Refresh() self.Screen.FootBar.ResetNavText() } } func (self *BleInfoPage) Click() { if self.PsIndex >= len(self.MyList) { return } cur_li := self.MyList[self.PsIndex] fmt.Println(cur_li.(*UI.InfoPageListItem).Flag) } func (self *BleInfoPage) OnLoadCb() { if self.Props.Connected == true { self.FootMsg[1] = "Disconnect" } else { self.FootMsg[1] = "" } self.GenList() } func (self *BleInfoPage) KeyDown(ev *event.Event) { if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] { self.ReturnToUpLevelPage() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["Up"] { self.ScrollUp() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["Down"] { self.ScrollDown() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["Enter"] { self.Click() } if ev.Data["Key"] == UI.CurKeys["X"] { self.TryToDisconnect() } if ev.Data["Key"] == UI.CurKeys["Y"] { self.TryToForget() } } func (self *BleInfoPage) Draw() { if len(self.MyList) == 0 { return } self.ClearCanvas() if len(self.MyList)*UI.DefaultInfoPageListItemHeight > self.Height { self.Ps.(*BleInfoPageSelector).Width = self.Width - 10 self.Ps.(*BleInfoPageSelector).PosX = 9 self.Ps.Draw() for _, v := range self.MyList { v.Draw() } self.Scroller.UpdateSize(len(self.MyList)*UI.DefaultInfoPageListItemHeight, self.PsIndex*UI.DefaultInfoPageListItemHeight) self.Scroller.Draw() } else { self.Ps.(*BleInfoPageSelector).Width = self.Width self.Ps.Draw() for _, v := range self.MyList { v.Draw() } } } type BleListMessageBox struct { UI.Label Parent UI.PageInterface } func NewBleListMessageBox() *BleListMessageBox { p := &BleListMessageBox{} p.Color = &color.Color{83, 83, 83, 255} return p } func (self *BleListMessageBox) Draw() { my_text := font.Render(self.FontObj, self.Text, true, self.Color, nil) w := surface.GetWidth(my_text) h := surface.GetHeight(my_text) pw, ph := self.Parent.Size() x := (pw - w) / 2 y := (ph - h) / 2 padding := 10 rect_ := rect.Rect(x-padding, y-padding, w+padding*2, h+padding*2) draw.Rect(self.CanvasHWND, &color.Color{255, 255, 255, 255}, &rect_, 0) draw.Rect(self.CanvasHWND, &color.Color{0, 0, 0, 255}, &rect_, 1) rect2_ := rect.Rect(x, y, w, h) surface.Blit(self.CanvasHWND, my_text, &rect2_, nil) my_text.Free() } type BluetoothPage struct { UI.Page Devices []*device.Device1 BlePassword string Connecting bool Scanning bool ListFontObj *ttf.Font Scroller *UI.ListScroller InfoPage *BleInfoPage PrevState int ShowingMessageBox bool MsgBox *BleListMessageBox ConnectTry int //BlockCb ?? LastStatusMsg string ADAPTER_DEV string // == adapterID Offline bool Leader *BluetoothPlugin } func NewBluetoothPage() *BluetoothPage { p := &BluetoothPage{} p.PageIconMargin = 20 p.SelectedIconTopOffset = 20 p.EasingDur = 10 p.Align = UI.ALIGN["SLeft"] p.ADAPTER_DEV = adapterID p.FootMsg = [5]string{"Nav", "Scan", "Info", "Back", "TryConnect"} p.ListFontObj = UI.Fonts["notosanscjk15"] return p } func (self *BluetoothPage) ShowBox(msg string) { self.MsgBox.Text = msg self.ShowingMessageBox = true self.Screen.ShowMsg(msg,0) } func (self *BluetoothPage) HideBox() { self.ShowingMessageBox = false self.Screen.Refresh() } func (self *BluetoothPage) Init() { self.PosX = self.Index * self.Screen.Width self.Width = self.Screen.Width self.Height = self.Screen.Height self.CanvasHWND = self.Screen.CanvasHWND ps := NewBleInfoPageSelector() ps.Parent = self ps.Width = UI.Width - 12 self.Ps = ps self.PsIndex = 0 msgbox := NewBleListMessageBox() msgbox.CanvasHWND = self.CanvasHWND msgbox.Init(" ", UI.Fonts["veramono12"], nil) msgbox.Parent = self self.MsgBox = msgbox self.Scroller = UI.NewListScroller() self.Scroller.Parent = self self.Scroller.PosX = 2 self.Scroller.PosY = 2 self.Scroller.Init() self.InfoPage = NewBleInfoPage() self.InfoPage.Screen = self.Screen self.InfoPage.Name = "BluetoothInfo" self.InfoPage.Init() } func (self *BluetoothPage) AbortedAndReturnToUpLevel() { self.HideBox() self.Screen.FootBar.ResetNavText() self.ReturnToUpLevelPage() self.Screen.Refresh() } func (self *BluetoothPage) TryConnect() { if self.PsIndex >= len(self.MyList) { return } cur_li := self.MyList[self.PsIndex] if cur_li.(*NetItem).Props.Connected { return } self.Screen.FootBar.UpdateNavText("Connecting") self.ShowBox("Connecting") self.Leader.PairPage.DevObj = cur_li.(*NetItem).Device err := cur_li.(*NetItem).Device.Pair() if err != nil { fmt.Println(err) err_msg := "" s := err.Error() err_msg = "Pair error" if strings.Contains(s, "ConnectionAttemptFailed") { err_msg = "Page Timeout" } if strings.Contains(s, "NoReply") { err_msg = "NoReply,Cancelling" dev1 := cur_li.(*NetItem).Device dev1.CancelPairing() } if strings.Contains(s, "Exists") { err_msg = "Already Exists" adapter, err := bleapi.GetAdapter(adapterID) if err == nil { err = adapter.RemoveDevice(cur_li.(*NetItem).Device.Path()) if err != nil { fmt.Println(err) } } else { fmt.Println(err) } } self.Leader.PairPage.PairErrorCb(err_msg) self.Leader.PairPage.DevObj = nil } else { self.Leader.PairPage.PairOKCb() dev1 := cur_li.(*NetItem).Device err = dev1.SetTrusted(true) if err != nil { fmt.Println(err) } cur_li.(*NetItem).Device.Connect() } self.HideBox() self.Screen.FootBar.ResetNavText() } //GetDevices returns a list of bluetooth discovered Devices func (self *BluetoothPage) GetDevices() ([]*device.Device1, error) { adapter, err := bleapi.GetAdapter(adapterID) if err != nil { return nil, err } list, err := adapter.GetDevices() return list, err } func (self *BluetoothPage) RefreshDevices() { // sync the cached devices self.Devices = self.Devices[:0] devices, err := self.GetDevices() if err != nil { panic(err) os.Exit(1) } self.Devices = devices } func (self *BluetoothPage) GenNetworkList() { self.MyList = nil start_x := 0 start_y := 0 for i, v := range self.Devices { // v == bleapi.Device props, err := v.GetProperties() if err != nil { log.Printf("%s: Failed to get properties: %s\n", v.Path, err.Error()) return } ni := NewNetItem() ni.Parent = self ni.PosX = start_x ni.PosY = start_y + i*NetItemDefaultHeight ni.Width = UI.Width ni.FontObj = self.ListFontObj ni.Props = props ni.Parent = self ni.Device = v if props.Name != "" { ni.Init(props.Name) } else { ni.Init(props.Address) } self.MyList = append(self.MyList, ni) } self.PsIndex = 0 } func (self *BluetoothPage) Rescan() { if self.Scanning == true { self.ShowBox("Bluetooth scanning") self.Screen.FootBar.UpdateNavText("Scanning") } a, err := adapter.GetAdapter(adapterID) if err != nil { fmt.Println(err) return } discovery, cancel, err := bleapi.Discover(a, nil) if err != nil { fmt.Println(err) } defer cancel() wait := make(chan error) self.Scanning = true self.ShowBox("Bluetooth scanning") self.Screen.FootBar.UpdateNavText("Scanning") go func() { for dev := range discovery { if dev == nil { return } wait <- nil } }() go func() { sleep := 5 gotime.Sleep(gotime.Duration(sleep) * gotime.Second) logrus.Debugf("Discovery timeout exceeded (%ds)", sleep) wait <- nil }() err = <-wait if err != nil { fmt.Println(err) } self.Scanning = false self.HideBox() self.Screen.FootBar.ResetNavText() } func (self *BluetoothPage) OnLoadCb() { self.Offline = false if self.Screen.TitleBar.InAirPlaneMode == false { out := UI.System("hcitool dev | grep hci0 |cut -f3") if len(out) < 17 { self.Offline = true fmt.Println("Bluetooth OnLoadCb ,can not find hci0 alive,try to reboot") } else { self.Rescan() self.RefreshDevices() self.GenNetworkList() } } else { self.Offline = true } } func (self *BluetoothPage) ScrollUp() { if len(self.MyList) == 0 { return } self.PsIndex -= 1 if self.PsIndex < 0 { self.PsIndex = 0 } cur_ni := self.MyList[self.PsIndex] //*NetItem if cur_ni.(*NetItem).PosY < 0 { for i := 0; i < len(self.MyList); i++ { self.MyList[i].(*NetItem).PosY += self.MyList[i].(*NetItem).Height } } } func (self *BluetoothPage) ScrollDown() { if len(self.MyList) == 0 { return } self.PsIndex += 1 if self.PsIndex >= len(self.MyList) { self.PsIndex = len(self.MyList) - 1 } cur_ni := self.MyList[self.PsIndex] if cur_ni.(*NetItem).PosY+cur_ni.(*NetItem).Height > self.Height { for i := 0; i < len(self.MyList); i++ { self.MyList[i].(*NetItem).PosY -= self.MyList[i].(*NetItem).Height } } } func (self *BluetoothPage) KeyDown(ev *event.Event) { if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] { if self.Offline == true { self.AbortedAndReturnToUpLevel() return } a, nil := bleapi.GetAdapter(adapterID) err := a.StopDiscovery() if err != nil { fmt.Println(err) } self.HideBox() self.ReturnToUpLevelPage() self.Screen.Refresh() self.Screen.FootBar.ResetNavText() } if ev.Data["Key"] == UI.CurKeys["Up"] { self.ScrollUp() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["Down"] { self.ScrollDown() self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["X"] { if self.Offline == false { self.Rescan() } } if ev.Data["Key"] == UI.CurKeys["Y"] { if len(self.MyList) == 0 { return } if self.Offline == true { return } self.InfoPage.Props = self.MyList[self.PsIndex].(*NetItem).Props self.InfoPage.Path = self.MyList[self.PsIndex].(*NetItem).Path self.InfoPage.MyDevice = self.MyList[self.PsIndex].(*NetItem).Device self.Screen.PushPage(self.InfoPage) self.Screen.Refresh() } if ev.Data["Key"] == UI.CurKeys["B"] { if self.Offline == false { self.TryConnect() } } } func (self *BluetoothPage) Draw() { self.ClearCanvas() if len(self.MyList) == 0 { return } if len(self.MyList)*NetItemDefaultHeight > self.Height { self.Ps.(*BleInfoPageSelector).Width = self.Width - 11 self.Ps.Draw() for _, v := range self.MyList { v.Draw() } self.Scroller.UpdateSize(len(self.MyList)*NetItemDefaultHeight, self.PsIndex*NetItemDefaultHeight) self.Scroller.Draw() } else { self.Ps.(*BleInfoPageSelector).Width = self.Width self.Ps.Draw() for _, v := range self.MyList { v.Draw() } } }