From c5bd510210db643d448af6f7616d3993fc7069ad Mon Sep 17 00:00:00 2001 From: clockworkpi <35560767+clockworkpi@users.noreply.github.com> Date: Fri, 25 May 2018 20:36:46 +0800 Subject: [PATCH] Add files via upload --- sys.py/UI/Emulator/__init__.py | 114 ++++ sys.py/UI/Emulator/fav_list_page.py | 415 ++++++++++++ sys.py/UI/Emulator/list_item.py | 111 ++++ sys.py/UI/Emulator/rom_list_page.py | 466 ++++++++++++++ sys.py/UI/Emulator/rom_so_confirm_page.py | 122 ++++ sys.py/UI/above_all_patch.py | 118 ++++ sys.py/UI/blueselector_b64.py | 2 + sys.py/UI/confirm_page.py | 142 ++++ sys.py/UI/constants.py | 45 ++ sys.py/UI/delete_confirm_page.py | 73 +++ sys.py/UI/download.py | 213 ++++++ sys.py/UI/download_process_page.py | 225 +++++++ sys.py/UI/fonts.py | 63 ++ sys.py/UI/foot_bar.py | 161 +++++ sys.py/UI/icon_item.py | 109 ++++ sys.py/UI/icon_pool.py | 29 + sys.py/UI/keyboard_keys.layout | 14 + sys.py/UI/keys_def.py | 58 ++ sys.py/UI/label.py | 56 ++ sys.py/UI/main_screen.py | 468 ++++++++++++++ sys.py/UI/multi_icon_item.py | 44 ++ sys.py/UI/multilabel.py | 86 +++ sys.py/UI/page.py | 606 ++++++++++++++++++ sys.py/UI/scroller.py | 61 ++ sys.py/UI/simple_name_space.py | 12 + sys.py/UI/slider.py | 40 ++ sys.py/UI/title_bar.py | 290 +++++++++ sys.py/UI/untitled_icon.py | 63 ++ sys.py/UI/util_funcs.py | 100 +++ sys.py/config.py | 16 + sys.py/gameshell/blank.png | Bin 0 -> 2101 bytes sys.py/gameshell/footbar_icons/footbar.png | Bin 0 -> 1085 bytes sys.py/gameshell/icons/about_bg.png | Bin 0 -> 12219 bytes sys.py/gameshell/icons/empty.png | Bin 0 -> 875 bytes sys.py/gameshell/icons/heart.png | Bin 0 -> 2266 bytes sys.py/gameshell/icons/icon_sd.png | Bin 0 -> 2146 bytes sys.py/gameshell/icons/light.png | Bin 0 -> 9328 bytes sys.py/gameshell/icons/needwifi_bg.png | Bin 0 -> 6672 bytes sys.py/gameshell/icons/online.png | Bin 0 -> 5721 bytes sys.py/gameshell/icons/rom_download.png | Bin 0 -> 545 bytes sys.py/gameshell/icons/scale.png | Bin 0 -> 1886 bytes sys.py/gameshell/icons/star.png | Bin 0 -> 2027 bytes sys.py/gameshell/icons/sys.png | Bin 0 -> 427 bytes sys.py/gameshell/icons/vol.png | Bin 0 -> 11833 bytes .../titlebar_icons/battery_unknown.png | Bin 0 -> 316 bytes .../ic_battery_charging_full_black.png | Bin 0 -> 334 bytes .../titlebar_icons/ic_volume_up_black.png | Bin 0 -> 353 bytes .../gameshell/titlebar_icons/soundvolume.png | Bin 0 -> 601 bytes sys.py/gameshell/titlebar_icons/wifi.png | Bin 0 -> 1918 bytes .../gameshell/titlebar_icons/withcharging.png | Bin 0 -> 673 bytes .../titlebar_icons/without_charging.png | Bin 0 -> 627 bytes sys.py/gameshell/wallpaper/gameover.png | Bin 0 -> 1056 bytes sys.py/gameshell/wallpaper/loading.png | Bin 0 -> 1788 bytes sys.py/gameshell/wallpaper/seeyou.png | Bin 0 -> 2322 bytes sys.py/libs/DBUS/__init__.py | 112 ++++ sys.py/libs/MPD/poller.py | 190 ++++++ sys.py/libs/easing.py | 7 + sys.py/libs/roundrects/__init__.py | 1 + sys.py/libs/roundrects/roundrects.py | 60 ++ sys.py/proc/cpuinfo | 43 ++ sys.py/proc/driver/backlight | 1 + sys.py/run.py | 324 ++++++++++ .../class/power_supply/axp20x-battery/uevent | 12 + 63 files changed, 5072 insertions(+) create mode 100644 sys.py/UI/Emulator/__init__.py create mode 100644 sys.py/UI/Emulator/fav_list_page.py create mode 100644 sys.py/UI/Emulator/list_item.py create mode 100644 sys.py/UI/Emulator/rom_list_page.py create mode 100644 sys.py/UI/Emulator/rom_so_confirm_page.py create mode 100644 sys.py/UI/above_all_patch.py create mode 100644 sys.py/UI/blueselector_b64.py create mode 100644 sys.py/UI/confirm_page.py create mode 100644 sys.py/UI/constants.py create mode 100644 sys.py/UI/delete_confirm_page.py create mode 100644 sys.py/UI/download.py create mode 100644 sys.py/UI/download_process_page.py create mode 100644 sys.py/UI/fonts.py create mode 100644 sys.py/UI/foot_bar.py create mode 100644 sys.py/UI/icon_item.py create mode 100644 sys.py/UI/icon_pool.py create mode 100644 sys.py/UI/keyboard_keys.layout create mode 100644 sys.py/UI/keys_def.py create mode 100644 sys.py/UI/label.py create mode 100644 sys.py/UI/main_screen.py create mode 100644 sys.py/UI/multi_icon_item.py create mode 100644 sys.py/UI/multilabel.py create mode 100644 sys.py/UI/page.py create mode 100644 sys.py/UI/scroller.py create mode 100644 sys.py/UI/simple_name_space.py create mode 100644 sys.py/UI/slider.py create mode 100644 sys.py/UI/title_bar.py create mode 100644 sys.py/UI/untitled_icon.py create mode 100644 sys.py/UI/util_funcs.py create mode 100644 sys.py/config.py create mode 100644 sys.py/gameshell/blank.png create mode 100644 sys.py/gameshell/footbar_icons/footbar.png create mode 100644 sys.py/gameshell/icons/about_bg.png create mode 100644 sys.py/gameshell/icons/empty.png create mode 100644 sys.py/gameshell/icons/heart.png create mode 100644 sys.py/gameshell/icons/icon_sd.png create mode 100644 sys.py/gameshell/icons/light.png create mode 100644 sys.py/gameshell/icons/needwifi_bg.png create mode 100644 sys.py/gameshell/icons/online.png create mode 100644 sys.py/gameshell/icons/rom_download.png create mode 100644 sys.py/gameshell/icons/scale.png create mode 100644 sys.py/gameshell/icons/star.png create mode 100644 sys.py/gameshell/icons/sys.png create mode 100644 sys.py/gameshell/icons/vol.png create mode 100644 sys.py/gameshell/titlebar_icons/battery_unknown.png create mode 100644 sys.py/gameshell/titlebar_icons/ic_battery_charging_full_black.png create mode 100644 sys.py/gameshell/titlebar_icons/ic_volume_up_black.png create mode 100644 sys.py/gameshell/titlebar_icons/soundvolume.png create mode 100644 sys.py/gameshell/titlebar_icons/wifi.png create mode 100644 sys.py/gameshell/titlebar_icons/withcharging.png create mode 100644 sys.py/gameshell/titlebar_icons/without_charging.png create mode 100644 sys.py/gameshell/wallpaper/gameover.png create mode 100644 sys.py/gameshell/wallpaper/loading.png create mode 100644 sys.py/gameshell/wallpaper/seeyou.png create mode 100644 sys.py/libs/DBUS/__init__.py create mode 100644 sys.py/libs/MPD/poller.py create mode 100644 sys.py/libs/easing.py create mode 100644 sys.py/libs/roundrects/__init__.py create mode 100644 sys.py/libs/roundrects/roundrects.py create mode 100644 sys.py/proc/cpuinfo create mode 100644 sys.py/proc/driver/backlight create mode 100644 sys.py/run.py create mode 100644 sys.py/sys/class/power_supply/axp20x-battery/uevent diff --git a/sys.py/UI/Emulator/__init__.py b/sys.py/UI/Emulator/__init__.py new file mode 100644 index 0000000..3d201bc --- /dev/null +++ b/sys.py/UI/Emulator/__init__.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + + +## local UI import +from UI.delete_confirm_page import DeleteConfirmPage +from UI.icon_pool import MyIconPool +from UI.keys_def import CurKeys + +from rom_list_page import RomListPage +from fav_list_page import FavListPage + +class FavDeleteConfirmPage(DeleteConfirmPage): + + def KeyDown(self,event): + + if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]: + + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + + if event.key == CurKeys["B"]: + try: + #self._FileName + stats = os.stat(self._FileName) + os.chown(self._FileName, stats.st_uid,stats.st_uid) ## normally uid and gid should be the same + except: + print("error in FavDeleteConfirmPage chown ") + + self.SnapMsg("Deleteing....") + self._Screen.Draw() + self._Screen.SwapAndShow() + self.Reset() + + pygame.time.delay(300) + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + print(self._FileName) + + +class MyEmulator(object): + + _Icons = {} + RomListPage = None + FavListPage = None + _Emulator = None + + _FavGID = 31415 + _FavGname = "cpifav" + + def __init__(self): + self._Icons = {} + + def load_icons(self): + """ + basepath = os.path.dirname(os.path.realpath(__file__)) + files = os.listdir(basepath+"/icons") + for i in files: + if os.path.isfile(basepath+"/"+i) and i.endswith(".png"): + keyname = i.split(".")[0] + self._Icons[keyname] = pygame.image.load(basepath+"/"+i).convert_alpha() + """ + self._Icons["sys"] = MyIconPool._Icons["sys"] + + + def InitDeleteConfirmPage(self,main_screen): + self.DeleteConfirmPage = DeleteConfirmPage() + self.DeleteConfirmPage._Screen = main_screen + self.DeleteConfirmPage._Name = "Delete Confirm" + self.DeleteConfirmPage.Init() + + self.FavDeleteConfirmPage = FavDeleteConfirmPage() + self.FavDeleteConfirmPage._Screen = main_screen + self.FavDeleteConfirmPage._Name = "Delete Confirm" + self.FavDeleteConfirmPage.Init() + + def InitFavListPage(self,main_screen): + self.FavListPage = FavListPage() + self.FavListPage._Screen = main_screen + self.FavListPage._Name = "Favourite Games" + self.FavListPage._Emulator = self._Emulator + self.FavListPage._Parent = self + + self.FavListPage.Init() + + def InitRomListPage(self,main_screen): + self.RomListPage = RomListPage() + self.RomListPage._Screen = main_screen + self.RomListPage._Name = self._Emulator["TITLE"] + self.RomListPage._Emulator = self._Emulator + self.RomListPage._Parent = self + self.RomListPage.Init() + + def Init(self,main_screen): + self.load_icons() + self.InitDeleteConfirmPage(main_screen) + self.InitRomListPage(main_screen) + self.InitFavListPage(main_screen) + + def API(self,main_screen): + if main_screen !=None: + main_screen.PushCurPage() + main_screen.SetCurPage(self.RomListPage) + main_screen.Draw() + main_screen.SwapAndShow() diff --git a/sys.py/UI/Emulator/fav_list_page.py b/sys.py/UI/Emulator/fav_list_page.py new file mode 100644 index 0000000..4692566 --- /dev/null +++ b/sys.py/UI/Emulator/fav_list_page.py @@ -0,0 +1,415 @@ +# -*- coding: utf-8 -*- + +import os +import pygame +import glob + +from libs.roundrects import aa_round_rect + +## local UI import +from UI.constants import Width,Height,ICON_TYPES,RUNEVT +from UI.page import Page,PageSelector +from UI.label import Label +from UI.icon_item import IconItem +from UI.fonts import fonts +from UI.util_funcs import midRect,CmdClean,FileExists +from UI.keys_def import CurKeys +from UI.multi_icon_item import MultiIconItem +from UI.icon_pool import MyIconPool +from UI.scroller import ListScroller + +from rom_so_confirm_page import RomSoConfirmPage + +from list_item import ListItem + + +class FavStack: + _Emulator = None + + def __init__(self): + self.stack = list() + + def Push(self,data): + if data not in self.stack: + self.stack.append(data) + return True + return False + + def Pop(self): + if len(self.stack)<=0: + return None,False + return self.stack.pop(),True + + def Last(self): + idx = len(self.stack) -1 + if idx < 0:## empty stack,return root path + return self._Emulator["ROM"] + else: + return self.stack[ idx ] + + def Length(self): + return len(self.stack) + +class ListPageSelector(PageSelector): + _BackgroundColor = pygame.Color(131,199,219) + + def __init__(self): + self._PosX = 0 + self._PosY = 0 + self._Height = 0 + self._Width = Width-12 + + def AnimateDraw(self,x2,y2): + pass + + def Draw(self): + idx = self._Parent._PsIndex + if idx > (len(self._Parent._MyList)-1): + idx = len(self._Parent._MyList) + if idx > 0: + idx -=1 + elif idx == 0:##nothing in _MyList + return + + self._Parent._PsIndex = idx #sync PsIndex + + x = self._Parent._MyList[idx]._PosX+2 + y = self._Parent._MyList[idx]._PosY+1 + h = self._Parent._MyList[idx]._Height -3 + + self._PosX = x + self._PosY = y + self._Height = h + + aa_round_rect(self._Parent._CanvasHWND, + (x,y,self._Width-4,h),self._BackgroundColor,4,0,self._BackgroundColor) + + + +class FavListPage(Page): + + _Icons = {} + _Selector=None + _FootMsg = ["Nav","Scan","Remove","","Run"] + _MyList = [] + _ListFont = fonts["notosanscjk15"] + _MyStack = None + _Emulator = None + _Parent = None + _Scroller = None + _Scrolled = 0 + _BGwidth = 75 + _BGheight = 73 + _RomSoConfirmDownloadPage = None + + + def __init__(self): + Page.__init__(self) + self._Icons = {} + self._CanvasHWND = None + self._MyList = [] + self._MyStack = FavStack() + self._Emulator = {} + + def GeneratePathList(self,path): + if os.path.isdir(path) == False: + return False + + + files_path = glob.glob(path+"/*") + + print(files_path) + ret = [] + + for i ,v in enumerate(files_path): + dirmap = {} + #if os.path.isdir(v): + # continue + # dir_base_name = os.path.basename(v) + # if dir_base_name == ".Trash" or dir_base_name == ".Fav": + # pass + # else: + # dirmap["directory"] = v + # ret.append(dirmap) + + if os.path.isfile(v): + stats = os.stat(v) + if stats.st_gid != self._Parent._FavGID: + continue + bname = os.path.basename(v) ### filter extension + if len(bname)> 1: + pieces = bname.split(".") + if len(pieces) > 1: + if pieces[ len(pieces)-1 ].lower() in self._Emulator["EXT"]: + dirmap["file"] = v + ret.append(dirmap) + + else: + print("not file or dir") + + + return ret + + def SyncList(self,path): + + alist = self.GeneratePathList(path) + + if alist == False: + print("listfiles return false") + return + print("fav list alist: ") + print(alist) + + self._MyList = [] + start_x = 0 + start_y = 0 + hasparent = 0 + if self._MyStack.Length() > 0: + hasparent = 1 + li = ListItem() + li._Parent = self + li._PosX = start_x + li._PosY = start_y + li._Width = Width + li._Fonts["normal"] = self._ListFont + li._MyType = ICON_TYPES["DIR"] + li._Parent = self + li.Init("[..]") + self._MyList.append(li) + + for i,v in enumerate(sorted(alist)): + li = ListItem() + li._Parent = self + li._PosX = start_x + li._PosY = start_y + (i+hasparent)*ListItem._Height + li._Width = Width + li._Fonts["normal"] = self._ListFont + li._MyType = ICON_TYPES["FILE"] + li._Parent = self + if "directory" in v: + li._MyType = ICON_TYPES["DIR"] + li.Init(v["directory"]) + elif "file" in v: + li.Init(v["file"]) + else: + li.Init("NoName") + + self._MyList.append(li) + + + + def Init(self): + self._PosX = self._Index * self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + self._CanvasHWND = self._Screen._CanvasHWND + + ps = ListPageSelector() + ps._Parent = self + self._Ps = ps + self._PsIndex = 0 + + self.SyncList(self._Emulator["ROM"]) + + self._MyStack._Emulator = self._Emulator + + icon_for_list = MultiIconItem() + icon_for_list._ImgSurf = self._Parent._Icons["sys"] + icon_for_list._MyType = ICON_TYPES["STAT"] + icon_for_list._Parent = self + icon_for_list.Adjust(0,0,18,18,0) + + self._Icons["sys"] = icon_for_list + + + bgpng = IconItem() + bgpng._ImgSurf = MyIconPool._Icons["star"] + bgpng._MyType = ICON_TYPES["STAT"] + bgpng._Parent = self + bgpng.AddLabel("my favourites games", fonts["varela18"]) + bgpng.SetLableColor(pygame.Color(204,204,204)) + bgpng.Adjust(0,0,self._BGwidth,self._BGheight,0) + + self._Icons["bg"] = bgpng + + self._Scroller = ListScroller() + self._Scroller._Parent = self + self._Scroller._PosX = self._Width - 10 + self._Scroller._PosY = 2 + self._Scroller.Init() + + rom_so_confirm_page = RomSoConfirmPage() + rom_so_confirm_page._Screen = self._Screen + rom_so_confirm_page._Name = "Download Confirm" + rom_so_confirm_page._Parent = self + rom_so_confirm_page.Init() + + self._RomSoConfirmDownloadPage = rom_so_confirm_page + + def ScrollUp(self): + if len(self._MyList) == 0: + return + + self._PsIndex -= 1 + if self._PsIndex < 0: + self._PsIndex = 0 + + cur_li = self._MyList[self._PsIndex] + if cur_li._PosY < 0: + for i in range(0, len(self._MyList)): + self._MyList[i]._PosY += self._MyList[i]._Height + + self._Scrolled += 1 + + def ScrollDown(self): + 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] + if cur_li._PosY +cur_li._Height > self._Height: + for i in range(0,len(self._MyList)): + self._MyList[i]._PosY -= self._MyList[i]._Height + self._Scrolled -=1 + def SyncScroll(self): + ## + if self._Scrolled == 0: + return + + if self._PsIndex < len(self._MyList): + cur_li = self._MyList[self._PsIndex] + if self._Scrolled > 0: + if cur_li._PosY < 0: + for i in range(0, len(self._MyList)): + self._MyList[i]._PosY += self._Scrolled * self._MyList[i]._Height + elif self._Scrolled < 0: + if cur_li._PosY +cur_li._Height > self._Height: + for i in range(0,len(self._MyList)): + self._MyList[i]._PosY += self._Scrolled * self._MyList[i]._Height + + def Click(self): + + cur_li = self._MyList[self._PsIndex] + + if cur_li._MyType == ICON_TYPES["DIR"]: + return + + if cur_li._MyType == ICON_TYPES["FILE"]: ## add to playlist only + self._Screen._MsgBox.SetText("Launching...") + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + print("Run ",cur_li._Path) + + # check ROM_SO exists + if FileExists(self._Emulator["ROM_SO"]): + escaped_path = CmdClean( cur_li._Path) + cmdpath = " ".join( (self._Emulator["LAUNCHER"],self._Emulator["ROM_SO"], escaped_path)) + pygame.event.post( pygame.event.Event(RUNEVT, message=cmdpath)) + return + else: + + self._Screen.PushPage(self._RomSoConfirmDownloadPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + return + + self._Screen.Draw() + self._Screen.SwapAndShow() + + def ReScan(self): + if self._MyStack.Length() == 0: + self.SyncList(self._Emulator["ROM"]) + else: + self.SyncList( self._MyStack.Last() ) + + idx = self._PsIndex + if idx > (len(self._MyList)-1): + idx = len(self._MyList) + if idx > 0: + idx -=1 + elif idx == 0:##nothing in _MyList + pass + + self._PsIndex = idx ## sync PsIndex + + self.SyncScroll() + + + def OnReturnBackCb(self): + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def OnLoadCb(self): + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def KeyDown(self,event): + + if event.key == CurKeys["Menu"] or event.key == CurKeys["Left"]: + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + + if event.key == CurKeys["Up"]: + self.ScrollUp() + self._Screen.Draw() + self._Screen.SwapAndShow() + if event.key == CurKeys["Down"]: + self.ScrollDown() + self._Screen.Draw() + self._Screen.SwapAndShow() + + + if event.key == CurKeys["Enter"]: + self.Click() + + + if event.key == CurKeys["X"]: #Scan current + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Y"]: #del + if len(self._MyList) == 0: + return + + cur_li = self._MyList[self._PsIndex] + if cur_li.IsFile(): + + self._Parent.FavDeleteConfirmPage.SetFileName(cur_li._Path) + self._Parent.FavDeleteConfirmPage.SetTrashDir(self._Emulator["ROM"])## Fav delete,return to ROM dir,not .Trash + + self._Screen.PushCurPage() + self._Screen.SetCurPage(self._Parent.FavDeleteConfirmPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + def Draw(self): + self.ClearCanvas() + + if len(self._MyList) == 0: + self._Icons["bg"].NewCoord(self._Width/2,self._Height/2) + self._Icons["bg"].Draw() + else: + if len(self._MyList) * ListItem._Height > self._Height: + self._Ps._Width = self._Width - 10 + self._Ps.Draw() + + for i in self._MyList: + i.Draw() + self._Scroller.UpdateSize( len(self._MyList)*ListItem._Height, self._PsIndex*ListItem._Height) + self._Scroller.Draw() + + else: + self._Ps._Width = self._Width + self._Ps.Draw() + for i in self._MyList: + i.Draw() diff --git a/sys.py/UI/Emulator/list_item.py b/sys.py/UI/Emulator/list_item.py new file mode 100644 index 0000000..ace4a6b --- /dev/null +++ b/sys.py/UI/Emulator/list_item.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +import pygame +#from beeprint import pp +import os + +## local UI import +from UI.constants import ICON_TYPES +from UI.page import Page +from UI.label import Label +from UI.fonts import fonts +from UI.icon_item import IconItem +from UI.util_funcs import midRect + +# a item for List +# - - - - - - - - - - - -- +# | Icon Text..... > | +# ------------------------ + + +class ListItemIcon(IconItem): + + _CanvasHWND = None + _Parent = None + _Width = 18 + _Height = 18 + + def Draw(self): + self._CanvasHWND.blit(self._ImgSurf,(self._PosX,self._PosY+(self._Parent._Height-self._Height)/2,self._Width,self._Height)) + +class ListItem(object): + _PosX = 0 + _PosY = 0 + _Width = 0 + _Height = 32 + + _Labels = {} + _Icons = {} + _Fonts = {} + _MyType = ICON_TYPES["EXE"] + _LinkObj = None + _Path = "" + _Active = False + _Playing = False ## play or pause + _Parent = None + + def __init__(self): + self._Labels = {} + self._Icons = {} + self._Fonts = {} + + def IsFile(self): + if self._MyType == ICON_TYPES["FILE"]: + return True + + return False + def IsDir(self): + if self._MyType == ICON_TYPES["DIR"]: + return True + + return False + + def Init(self,text): + + #self._Fonts["normal"] = fonts["veramono12"] + + l = Label() + l._PosX = 20 + l.SetCanvasHWND(self._Parent._CanvasHWND) + + if self._MyType == ICON_TYPES["DIR"] or self._MyType == ICON_TYPES["FILE"]: + self._Path = text + + label_text = os.path.basename(text) + + if self._MyType == ICON_TYPES["DIR"]: + l.Init(label_text,self._Fonts["normal"]) + else: + l.Init(label_text,self._Fonts["normal"]) + + + self._Labels["Text"] = l + + + + def Draw(self): + if self._Path != "[..]": + self._Labels["Text"]._PosX = 23 + else: + self._Labels["Text"]._PosX = 3 + + self._Labels["Text"]._PosY = self._PosY + (self._Height - self._Labels["Text"]._Height)/2 + self._Labels["Text"].Draw() + + """ + if self._Active == True: + pass + """ + if self._MyType == ICON_TYPES["DIR"] and self._Path != "[..]": + self._Parent._Icons["sys"]._IconIndex = 0 + self._Parent._Icons["sys"].NewCoord(self._PosX+12,self._PosY+ (self._Height - self._Parent._Icons["sys"]._Height)/2+self._Parent._Icons["sys"]._Height/2) + self._Parent._Icons["sys"].Draw() + + if self._MyType == ICON_TYPES["FILE"]: + self._Parent._Icons["sys"]._IconIndex = 1 + self._Parent._Icons["sys"].NewCoord(self._PosX+12,self._PosY+ (self._Height - self._Parent._Icons["sys"]._Height)/2+self._Parent._Icons["sys"]._Height/2) + self._Parent._Icons["sys"].Draw() + + pygame.draw.line(self._Parent._CanvasHWND,(169,169,169),(self._PosX,self._PosY+self._Height-1),(self._PosX+self._Width,self._PosY+self._Height-1),1) + + diff --git a/sys.py/UI/Emulator/rom_list_page.py b/sys.py/UI/Emulator/rom_list_page.py new file mode 100644 index 0000000..d27412a --- /dev/null +++ b/sys.py/UI/Emulator/rom_list_page.py @@ -0,0 +1,466 @@ +# -*- coding: utf-8 -*- + +import os +import pygame + +import glob +import shutil +import gobject +import validators +#from pySmartDL import SmartDL + + +from libs.roundrects import aa_round_rect + +## local UI import +from UI.constants import Width,Height,ICON_TYPES,RUNEVT +from UI.page import Page,PageSelector +from UI.label import Label +from UI.icon_item import IconItem +from UI.fonts import fonts +from UI.util_funcs import midRect,CmdClean,FileExists +from UI.keys_def import CurKeys +from UI.multi_icon_item import MultiIconItem +from UI.icon_pool import MyIconPool +from UI.scroller import ListScroller + +from rom_so_confirm_page import RomSoConfirmPage + +from list_item import ListItem +import config + +class RomStack: + _Emulator = None + + def __init__(self): + self.stack = list() + + def Push(self,data): + if data not in self.stack: + self.stack.append(data) + return True + return False + + def Pop(self): + if len(self.stack)<=0: + return None,False + return self.stack.pop(),True + + def Last(self): + idx = len(self.stack) -1 + if idx < 0:## empty stack,return root path + return self._Emulator["ROM"] + else: + return self.stack[ idx ] + + def Length(self): + return len(self.stack) + +class ListPageSelector(PageSelector): + _BackgroundColor = pygame.Color(131,199,219) + + def __init__(self): + self._PosX = 0 + self._PosY = 0 + self._Height = 0 + self._Width = Width-12 + + def AnimateDraw(self,x2,y2): + pass + + def Draw(self): + idx = self._Parent._PsIndex + if idx > (len(self._Parent._MyList)-1): + idx = len(self._Parent._MyList) + if idx > 0: + idx -=1 + elif idx == 0:##nothing in _MyList + return + + self._Parent._PsIndex = idx ## sync PsIndex + + x = self._Parent._MyList[idx]._PosX+2 + y = self._Parent._MyList[idx]._PosY+1 + h = self._Parent._MyList[idx]._Height -3 + + self._PosX = x + self._PosY = y + self._Height = h + + aa_round_rect(self._Parent._CanvasHWND, + (x,y,self._Width-4,h),self._BackgroundColor,4,0,self._BackgroundColor) + + + +class RomListPage(Page): + + _Icons = {} + _Selector=None + _FootMsg = ["Nav","Scan","Del","Add Fav","Run"] + _MyList = [] + _ListFont = fonts["notosanscjk15"] + _MyStack = None + _Emulator = None + _Parent = None + + _Scroller = None + + _Scrolled = 0 + + _BGwidth = 56 + _BGheight = 70 + + _RomSoConfirmDownloadPage = None + + + def __init__(self): + Page.__init__(self) + + self._Icons = {} + self._CanvasHWND = None + self._MyList = [] + self._MyStack = RomStack() + self._Emulator = {} + + def GeneratePathList(self,path): + if os.path.isdir(path) == False: + return False + + + files_path = glob.glob(path+"/*") + + ret = [] + + for i ,v in enumerate(files_path): + dirmap = {} + #if os.path.isdir(v): + # dir_base_name = os.path.basename(v) + # if dir_base_name == ".Trash" or dir_base_name == ".Fav": + # pass + # else: + # dirmap["directory"] = v + # ret.append(dirmap) + if os.path.isfile(v): + stats = os.stat(v) + if stats.st_gid == self._Parent._FavGID: + continue + + bname = os.path.basename(v) ### filter extension + if len(bname)> 1: + pieces = bname.split(".") + if len(pieces) > 1: + if pieces[ len(pieces)-1 ].lower() in self._Emulator["EXT"]: + dirmap["file"] = v + ret.append(dirmap) + else: + print("not file or dir") + + + return ret + + def SyncList(self,path): + + alist = self.GeneratePathList(path) + + if alist == False: + print("listfiles return false") + return + print("rom list alist: ") + print(alist) + + self._MyList = [] + start_x = 0 + start_y = 0 + hasparent = 0 + if self._MyStack.Length() > 0: + hasparent = 1 + li = ListItem() + li._Parent = self + li._PosX = start_x + li._PosY = start_y + li._Width = Width + li._Fonts["normal"] = self._ListFont + li._MyType = ICON_TYPES["DIR"] + li._Parent = self + li.Init("[..]") + self._MyList.append(li) + + for i,v in enumerate(sorted(alist)): + li = ListItem() + li._Parent = self + li._PosX = start_x + li._PosY = start_y + (i+hasparent)*ListItem._Height + li._Width = Width + li._Fonts["normal"] = self._ListFont + li._MyType = ICON_TYPES["FILE"] + + li._Parent = self + + if "directory" in v: + li._MyType = ICON_TYPES["DIR"] + li.Init(v["directory"]) + elif "file" in v: + li.Init(v["file"]) + else: + li.Init("NoName") + + + self._MyList.append(li) + + + def Init(self): + self._PosX = self._Index * self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + self._CanvasHWND = self._Screen._CanvasHWND + + ps = ListPageSelector() + ps._Parent = self + self._Ps = ps + self._PsIndex = 0 + + self.SyncList(self._Emulator["ROM"]) + + ### will also mkdir of the ***ROM self + try: + os.makedirs(self._Emulator["ROM"]+"/.Trash") + except OSError: + if not os.path.isdir(self._Emulator["ROM"]+"/.Trash"): + raise + + + try: + os.makedirs(self._Emulator["ROM"]+"/.Fav") + except OSError: + if not os.path.isdir(self._Emulator["ROM"]+"/.Fav"): + raise + + self._MyStack._Emulator = self._Emulator + + icon_for_list = MultiIconItem() + icon_for_list._ImgSurf = self._Parent._Icons["sys"] + icon_for_list._MyType = ICON_TYPES["STAT"] + icon_for_list._Parent = self + + icon_for_list.Adjust(0,0,18,18,0) + + self._Icons["sys"] = icon_for_list + + + bgpng = IconItem() + bgpng._ImgSurf = MyIconPool._Icons["empty"] + bgpng._MyType = ICON_TYPES["STAT"] + bgpng._Parent = self + bgpng.AddLabel("Please upload data over Wi-Fi", fonts["varela22"]) + bgpng.SetLableColor(pygame.Color(204,204,204)) + bgpng.Adjust(0,0,self._BGwidth,self._BGheight,0) + + self._Icons["bg"] = bgpng + + self._Scroller = ListScroller() + self._Scroller._Parent = self + self._Scroller._PosX = self._Width - 10 + self._Scroller._PosY = 2 + self._Scroller.Init() + + rom_so_confirm_page = RomSoConfirmPage() + rom_so_confirm_page._Screen = self._Screen + rom_so_confirm_page._Name = "Download Confirm" + rom_so_confirm_page._Parent = self + rom_so_confirm_page.Init() + + self._RomSoConfirmDownloadPage = rom_so_confirm_page + + def ScrollUp(self): + if len(self._MyList) == 0: + return + + self._PsIndex -= 1 + if self._PsIndex < 0: + self._PsIndex = 0 + cur_li = self._MyList[self._PsIndex] + if cur_li._PosY < 0: + for i in range(0, len(self._MyList)): + self._MyList[i]._PosY += self._MyList[i]._Height + self._Scrolled +=1 + + def ScrollDown(self): + 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] + if cur_li._PosY +cur_li._Height > self._Height: + for i in range(0,len(self._MyList)): + self._MyList[i]._PosY -= self._MyList[i]._Height + self._Scrolled -= 1 + + def SyncScroll(self): + ## + if self._Scrolled == 0: + return + + if self._PsIndex < len(self._MyList): + cur_li = self._MyList[self._PsIndex] + if self._Scrolled > 0: + if cur_li._PosY < 0: + for i in range(0, len(self._MyList)): + self._MyList[i]._PosY += self._Scrolled * self._MyList[i]._Height + elif self._Scrolled < 0: + if cur_li._PosY +cur_li._Height > self._Height: + for i in range(0,len(self._MyList)): + self._MyList[i]._PosY += self._Scrolled * self._MyList[i]._Height + + def Click(self): + if len(self._MyList) == 0: + return + + cur_li = self._MyList[self._PsIndex] + + if cur_li._MyType == ICON_TYPES["DIR"]: + if cur_li._Path == "[..]": + self._MyStack.Pop() + self.SyncList( self._MyStack.Last() ) + self._PsIndex = 0 + else: + self._MyStack.Push( self._MyList[self._PsIndex]._Path ) + self.SyncList( self._MyStack.Last() ) + self._PsIndex = 0 + + if cur_li._MyType == ICON_TYPES["FILE"]: ## add to playlist only + self._Screen._MsgBox.SetText("Launching...") + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + print("Run ",cur_li._Path) + + # check ROM_SO exists + if FileExists(self._Emulator["ROM_SO"]): + escaped_path = CmdClean( cur_li._Path) + cmdpath = " ".join( (self._Emulator["LAUNCHER"],self._Emulator["ROM_SO"], escaped_path)) + pygame.event.post( pygame.event.Event(RUNEVT, message=cmdpath)) + return + else: + + self._Screen.PushPage(self._RomSoConfirmDownloadPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + self._Screen.Draw() + self._Screen.SwapAndShow() + + def ReScan(self): + if self._MyStack.Length() == 0: + self.SyncList(self._Emulator["ROM"]) + else: + self.SyncList( self._MyStack.Last() ) + + + idx = self._PsIndex + if idx > (len(self._MyList)-1): + idx = len(self._MyList) + if idx > 0: + idx -=1 + elif idx == 0:##nothing in _MyList + pass + + self._PsIndex = idx ## sync PsIndex + + self.SyncScroll() + + def OnReturnBackCb(self): + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def KeyDown(self,event): + + if event.key == CurKeys["Menu"] : + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Right"]: + self._Screen.PushCurPage() + self._Screen.SetCurPage(self._Parent.FavListPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Up"]: + self.ScrollUp() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Down"]: + self.ScrollDown() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Enter"]: + self.Click() + + if event.key == CurKeys["A"]: + if len(self._MyList) == 0: + return + + cur_li = self._MyList[self._PsIndex] + if cur_li.IsFile(): + # remove any dup first + + try: + os.system("chgrp " + self._Parent._FavGname +" "+ CmdClean(cur_li._Path)) + except: + pass + + self._Screen._MsgBox.SetText("Adding to Favourite list") + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + + pygame.time.delay(600) + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["X"]: #Scan current + self.ReScan() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Y"]: #del + if len(self._MyList) == 0: + return + + cur_li = self._MyList[self._PsIndex] + if cur_li.IsFile(): + + self._Parent.DeleteConfirmPage.SetFileName(cur_li._Path) + self._Parent.DeleteConfirmPage.SetTrashDir(self._Emulator["ROM"]+"/.Trash") + + self._Screen.PushCurPage() + self._Screen.SetCurPage(self._Parent.DeleteConfirmPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + def Draw(self): + self.ClearCanvas() + if len(self._MyList) == 0: + self._Icons["bg"].NewCoord(self._Width/2,self._Height/2) + self._Icons["bg"].Draw() + else: + if len(self._MyList) * ListItem._Height > self._Height: + self._Ps._Width = self._Width - 10 + self._Ps.Draw() + + for i in self._MyList: + i.Draw() + self._Scroller.UpdateSize( len(self._MyList)*ListItem._Height, self._PsIndex*ListItem._Height) + self._Scroller.Draw() + + else: + self._Ps._Width = self._Width + self._Ps.Draw() + for i in self._MyList: + i.Draw() diff --git a/sys.py/UI/Emulator/rom_so_confirm_page.py b/sys.py/UI/Emulator/rom_so_confirm_page.py new file mode 100644 index 0000000..21e02da --- /dev/null +++ b/sys.py/UI/Emulator/rom_so_confirm_page.py @@ -0,0 +1,122 @@ + +# -*- coding: utf-8 -*- + +import os +import pygame + +import glob +import shutil +import gobject +import validators +#from pySmartDL import SmartDL + +from libs.roundrects import aa_round_rect + +from UI.confirm_page import ConfirmPage +from UI.download_process_page import DownloadProcessPage +from UI.keys_def import CurKeys +from UI.fonts import fonts +from UI.multilabel import MultiLabel + +import config + +class RomSoConfirmPage(ConfirmPage): + _ListFont = fonts["veramono18"] + + _ConfirmText = "Do you want to setup this game engine automatically?" + + _MyDownloadPage = None + + def CheckBattery(self): + try: + f = open(config.Battery) + except IOError: + print( "RomSoConfirmPage open %s failed" % config.Battery) + return 0 + else: + with f: + bat_uevent = {} + content = f.readlines() + content = [x.strip() for x in content] + for i in content: + pis = i.split("=") + if len(pis) > 1: + bat_uevent[pis[0]] = pis[1] + + if "POWER_SUPPLY_CAPACITY" in bat_uevent: + cur_cap = int(bat_uevent["POWER_SUPPLY_CAPACITY"]) + else: + cur_cap = 0 + + return cur_cap + + return 0 + + def Init(self): + self._PosX = self._Index * self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + self._CanvasHWND = self._Screen._CanvasHWND + + li = MultiLabel() + li.SetCanvasHWND(self._CanvasHWND) + li._Width = 160 + li.Init(self._ConfirmText,self._ListFont) + + li._PosX = (self._Width - li._Width)/2 + li._PosY = (self._Height - li._Height)/2 + + self._BGPosX = li._PosX-20 + self._BGPosY = li._PosY-20 + self._BGWidth = li._Width+40 + self._BGHeight = li._Height+40 + + self._MyList.append(li) + + def SnapMsg(self,msg): + self._MyList[0].SetText(msg) + self._Screen.Draw() + self._Screen.SwapAndShow() + self._MyList[0].SetText(self._ConfirmText) + + def OnReturnBackCb(self): + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def KeyDown(self,event): + if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]: + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["B"]: + if self.CheckBattery() < 5: + self.SnapMsg("Battery must over 5%") + else: + if self._MyDownloadPage == None: + self._MyDownloadPage = DownloadProcessPage() + self._MyDownloadPage._Screen = self._Screen + self._MyDownloadPage._Name = "Downloading..." + self._MyDownloadPage.Init() + + self._Screen.PushPage(self._MyDownloadPage) + self._Screen.Draw() + self._Screen.SwapAndShow() + + if config.CurKeySet == "PC": + so_url = self._Parent._Emulator["SO_URL"] ## [rom/fav]_list_page is _Parent + so_url = so_url.replace("armhf","x86_64") + print(so_url) + self._MyDownloadPage.StartDownload(so_url,os.path.dirname(self._Parent._Emulator["ROM_SO"])) + else: + self._MyDownloadPage.StartDownload(self._Parent._Emulator["SO_URL"], + os.path.dirname(self._Parent._Emulator["ROM_SO"])) + + + def Draw(self): + self.ClearCanvas() + self.DrawBG() + for i in self._MyList: + i.Draw() diff --git a/sys.py/UI/above_all_patch.py b/sys.py/UI/above_all_patch.py new file mode 100644 index 0000000..5cfcf17 --- /dev/null +++ b/sys.py/UI/above_all_patch.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- + +import pygame + +from libs.roundrects import aa_round_rect + +import alsaaudio + +## local package import +from constants import ICON_TYPES,icon_ext,icon_width,icon_height,RUNEVT +from icon_item import IconItem +from page import Page,PageStack +from title_bar import TitleBar +from foot_bar import FootBar +from constants import Width,Height,bg_color +from util_funcs import midRect +from fonts import fonts +from keys_def import CurKeys +from label import Label + + + + +class AboveAllPatch(object): + _PosX =Width/2 + _PosY =Height/2 + _Width =50 + _Height=120 + + _Text ="" + _FontObj=fonts["veramono20"] + _Parent =None + _Color = pygame.Color(83,83,83) + _ValColor= pygame.Color(0,0,255) + _CanvasHWND = None + _TextSurf = None + _Icons = {} + _Value = 0 + + def __init__(self): + self._Icons = {} + + def Init(self): + pass + + def SetCanvasHWND(self,_canvashwnd): + self._CanvasHWND = _canvashwnd + + def Draw(self): + start_rect = midRect(self._PosX,self._PosY,self._Width,self._Height,Width,Height) + aa_round_rect(self._CanvasHWND,start_rect, self._Color,3,0, self._Color) + + if self._Value > 10: + vol_height = int(self._Height * (float( self._Value)/100.0)) + dheight = self._Height - vol_height + + vol_rect = pygame.Rect(self._PosX-self._Width/2, self._PosY-self._Height/2+dheight, self._Width, vol_height) + + aa_round_rect(self._CanvasHWND,vol_rect, self._ValColor,3,0, self._ValColor) + + else: + vol_height = 10 + dheight = self._Height - vol_height + vol_rect = pygame.Rect(self._PosX-self._Width/2, self._PosY-self._Height/2+dheight, self._Width, vol_height) + + aa_round_rect(self._CanvasHWND,vol_rect, self._ValColor,3,0, self._ValColor) + + +class SoundPatch(AboveAllPatch): + + _ValColor = pygame.Color(131,199,219) + _Segs = [0,15,29, 45,55,65, 75,90,100] + snd_segs = [ [0,20],[21,40],[41,50],[51,60],[61,70],[71,85],[86,90],[91,95],[96,100] ] + + def Init(self): + self.SetCanvasHWND(self._Parent._CanvasHWND) + + def VolumeUp(self): + m = alsaaudio.Mixer() + vol = m.getvolume()[0] + vol += 10 + if vol > 100: + vol = 100 + + m.setvolume(vol) + + self._Value = vol + + return vol + + def VolumeDown(self): + m = alsaaudio.Mixer() + vol = m.getvolume()[0] + vol -= 10 + if vol < 0: + vol = 0 + m.setvolume(vol) + + print(vol) + self._Value = vol + return vol + + + def Draw(self): + # 200 total width + # h = 40 + ge = 0 + for i,v in enumerate(self.snd_segs): + if self._Value >= v[0] and self._Value <= v[1]: + ge = i + break + + for i in range(0,ge+1): + #w = 10,h = 40 + vol_rect = pygame.Rect(80+i*20, self._Height/2+20,10, 40) + + aa_round_rect(self._CanvasHWND,vol_rect, self._ValColor,3,0, self._ValColor) + diff --git a/sys.py/UI/blueselector_b64.py b/sys.py/UI/blueselector_b64.py new file mode 100644 index 0000000..64f0794 --- /dev/null +++ b/sys.py/UI/blueselector_b64.py @@ -0,0 +1,2 @@ +blueselector="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AAAAAAC77g8Av/8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC375gAt+7mALfu5gC37uYAt+7mALfvmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQC49RkAqv8DAAAAAAAAAAAAt+1xALfu5AC37tsAt+9cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfunwC37uYAt+7mALfu5gC37sIAte8fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALr/CwC37+IAt+7mALfu5gC37uYAt+98AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAue8vALfuzwC37uQAt++NALP/CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqv8DALfu2AC37uYAt+7mALfu5gC37uYAufAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALjwUgC37oMAtvE0AAAAAAAAAAAAqv8DALb/BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuPBEALbu3AC37uYAt+7mALfu5gC28EIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC473kAt+7mALfu5gC37uEAt+8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+8uALfvxgC37uYAt+6iAKr/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuO5aALfu5gC37uYAt+7mALfu5gC47ksAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqv8DALbtDgAAAAAAAAAAAAAAAAC37yAAvPITAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC27ssAt+7mALfu5gC37uYAt+/TAL/zFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu6FALfu5gC37uEAt+5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfusAC37uYAt+7mALfu2wC48DIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMT/DQC37uQAt+7mALfu5gC27eMAuO45AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuPEkALfunwC37qUAue8vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzP8FALbt1QC37uYAt+7mALfu5gC37s0Aqv8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtvFGALfu5QC37uYAt+7mALbt4wDH/wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuvI3ALfuzAC27tkAt+9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+9qALfu5QC37uYAt+6HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALr/CwC37swAt+7mALfu5gC37uEAxP8NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+8gALfvLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+98ALfu5gC37uYAt+7mALbu4AC//wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADV/wYAuO45AL/zFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC378UAt+7mALfu5gC37uYAt+98AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs/8KALfuvwC37uYAtu7cALnwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbukwC37uYAt+7mALfvxgDH/wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC27kkAt+7mALfu5gC37uYAt++3AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs/8KALfvbgC373wAvfYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnzKAC37uUAt+7mALfu5gC37uYAufFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbunQC37uYAt+7mALfu5gC37nUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKr/AwC47loAtu5zALfvIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvPAiALfu2wC37uYAtu7gALbuLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+51ALfu5gC37uYAtu7SALP/CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAue4dALjuVgC37yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANX/BgC37tYAt+7mALfu5gC37uYAtu4tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuO5IALfu5gC37uYAt+7mALfu1gDV/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu+4PALfvagC372oAxP8NAAAAAAAAAAAAAAAAAAAAAAC3718At+7mALfu5gC37uYAtu6FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC276gAt+7mALfu5gC376YAAAAAAAAAAAAAAAAAAAAAALTwEQC37sIAt+7mALfuzwC99hsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfu3wC37uYAt+7mALfu5QC99hsAAAAAAAAAAAAAAAAAAAAAAMz/BQC78ykAx/8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+60ALfu5gC37uYAt+7mALjvXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC472sAt+7mALfu5gC37uYAtu5zAAAAAAAAAAAAuvQWALjvawC4708A//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL/zFAC27ssAt+7mALfv0wC37yAAAAAAAL//BAC37sMAt+7mALfu5gC37nUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALP/CgC28TQAu+4PAAAAAAAAAAAAtvAxALfu5gC37uYAt+7mALfvtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC47lkAt+7mALfu5gC37uYAtu6dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtv8HALfwQwC67iwAAAAAALfvagC37uYAt+7mALfu5gC27nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AgC37rMAt+7mALfu5gC27lcAuvAhALfu2AC37uYAt+7YALfvIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtvEjALfu5gC37uYAt+7mALfutAAAAAAAtfUYALnvTAC49RkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC57h0At+7mALfu5gC37uYAt+7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANX/BgC37uQAt+7mALfu5gC37uEAAAAAAL/vEAC//wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbupAC37uYAt+7mALbuoQC271sAt+7hALfv4gC2714AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuv8LALbuZQC47mQAv/8MALfu2AC37uYAt+7mALfu2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+7mALfu5gC37uYAt+7mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC37uEAt+7mALfu5gC37uYAv/8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuO45ALfuvwC37sIAt+48ALbusgC37uYAt+7mALfuwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC378YAt+7mALfu5gC27s4Aue4dALfulQC375sAu/MpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtv8HALfu5gC37uYAt+7mALbt4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC57h0At+7mALfu5gC37uYAt+7RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbtDgC37uEAt+7mALfu5gC276gAAAAAALbvPwC37qIAtu6FALbtDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALjuWQC37swAt+67ALvzKQC88hMAt+7eALfu5gC37uYAt++JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbuLQC37uYAt+7mALfu5gC37skAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtv8HALfv4gC37uYAt+7mALfu5gC/8xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+48ALfvjQC3718A//8BAAAAAAAAAAAAtu6yALfu5gC37uYAt+7lAL30FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfvIAC37uEAt+7mALfu5gC371wAAAAAAAAAAAC47jkAt+/UALbt4wC37ngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+1xALfu5gC37uYAt+7mALjviwAAAAAAAAAAAAAAAAC//wQAs/8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC37rEAt+7mALfu5gC37uYAuO5aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbuwAC37uYAt+7mALfu5AC99BcAAAAAAAAAAAAAAAAAAAAAALTwEQC3738Atu6PALvzKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuO9PALfu4QC37uUAt+98AAAAAAAAAAAAAAAAAAAAAAAAAAAAt++bALfu5gC37uYAt+7QAL//CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv+8QALv2GgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC37sEAt+7mALfu5gC37uYAtu9bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALjvfQC37uYAt+7mALfu5gC37rEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALjuSwC37aoAtu6FAMf/CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+8gALfv4gC37uYAt+7mALbvtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC78ykAtu3jALfu5gC37uYAuO95AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+5nALbu4AC379MAue46AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfuoAC37uYAt+7mALfu5gC375gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzP8FAKr/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC99BcAt+7lALfu5gC37uYAt+7mALnxRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC17x8Ate8fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL/vEAC37uEAt+7mALfu5gC37rQA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu5zALfu5gC37uUAtu9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbwVAC27sAAt+6xALbxIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC37t8At+7mALfu5gC27eMAtvE0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfuwwC37uYAt+7mALfu5gC37r4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQC27lcAtu6BALfvLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu8/ALfu5QC37uYAt+7mALfu5QDV/wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu53ALfu5gC37uYAt+7dALX1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+6CALfu5gC37uYAt++XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnxRQC37uUAt+7mALfu5gC37uYAt+9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAufAzALnvPgD//wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu65ALfu5gC37uYAt+7mALbuzgC//wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADV/wYAt+5nALfvfwC37yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC376cAt+7mALfu5gC37uEAuPE2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfvfAC37uYAt+7mALfu2AC57y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfyJwC37q4At+68ALjuSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+6wALfu5gC37uYAt+7mALfu2AC88CIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC48RIAtvBUALXyJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2714At+7mALfu5gC37uYAt+7mALftcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAufAzALfu0QC37uYAt+7hALfuPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC473kAt+7mALfu5gC37uYAt/BRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbuOwC37t4At+7mALfu5gC37uYAt+2OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC27lcAt+6fALjuaAD//wEAAAAAAAAAAAC/8xQA//8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtu+oALfu5gC37uYAt+7mALfu5gC27lcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz/woAtu6yALfu5gC37sEAufAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC275oAt+7mALfu5gC37uYAt+54AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbuVwC37uYAt+7mALfu5gC37uQAtu5pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADM/wUAtvBCALryNwAAAAAAAAAAAL30FwC37qAAtu65ALjvTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+1HALfu5gC37uYAt+7mALfu5gC27eMAu+4PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+9AALfuwwC37uYAt+7mALftcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAt+6CALfu5QC37uYAt+7mALfu4QDE/w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALjuWQC37uYAt+7mALfu5gC37uYAt+/GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAue8+ALfuvAC27s4AuO5WAAAAAAAAAAAAtu9QALfvxQC27rIAtu9bAMf/CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+2qALfu5gC37uYAt+7mALfu5gC375gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALbtDgC27uAAt+7mALfu5gC37uYAtu7OALXvHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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ALfu5gC37uYAt+7bAMz/BQC37nUAt+7mALfu5gC37uYAt+7mALftuACq/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtv8HALvzKQC//wwAAAAAAAAAAAAAAAAAu/MpALbuvQC37uEAt+7mALfu0QC3704AAAAAAMz/BQC27nMAt++YALfvfwC27lcAv/8MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" + diff --git a/sys.py/UI/confirm_page.py b/sys.py/UI/confirm_page.py new file mode 100644 index 0000000..28f798f --- /dev/null +++ b/sys.py/UI/confirm_page.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- + +import pygame +import os + +from libs.roundrects import aa_round_rect + +#UI lib +from constants import Width,Height,ICON_TYPES +from page import Page,PageSelector +from label import Label +from fonts import fonts +from util_funcs import midRect +from keys_def import CurKeys + + + +class ListPageSelector(PageSelector): + _BackgroundColor = pygame.Color(131,199,219) + + def __init__(self): + self._PosX = 0 + self._PosY = 0 + self._Height = 0 + self._Width = Width + + def AnimateDraw(self,x2,y2): + pass + + def Draw(self): + idx = self._Parent._PsIndex + if idx > (len(self._Parent._MyList)-1): + idx = len(self._Parent._MyList) + if idx > 0: + idx -=1 + elif idx == 0: #Nothing + return + + x = self._Parent._MyList[idx]._PosX+2 + y = self._Parent._MyList[idx]._PosY+1 + h = self._Parent._MyList[idx]._Height -3 + + self._PosX = x + self._PosY = y + self._Height = h + + aa_round_rect(self._Parent._CanvasHWND, + (x,y,self._Width-4,h),self._BackgroundColor,4,0,self._BackgroundColor) + + + +class ConfirmPage(Page): + + _Icons = {} + _Selector=None + _FootMsg = ["Nav","","","Cancel","Yes"] + _MyList = [] + _ListFont = fonts["veramono20"] + _MyStack = None + _FileName = "" + _TrashDir = "" + _ConfirmText = "Confirm?" + _BGPosX = 0 + _BGPosY = 0 + _BGWidth = 0 + _BGHeight = 0 + _Parent = None + + def __init__(self): + Page.__init__(self) + self._Icons = {} + self._CanvasHWND = None + self._MyList = [] + + def Reset(self): + self._MyList[0].SetText(self._ConfirmText) + self._MyList[0]._PosX = (self._Width - self._MyList[0]._Width)/2 + self._MyList[0]._PosY = (self._Height - self._MyList[0]._Height)/2 + + self._BGPosX = self._MyList[0]._PosX-10 + self._BGPosY = self._MyList[0]._PosY-10 + self._BGWidth = self._MyList[0]._Width+20 + self._BGHeight = self._MyList[0]._Height+20 + + + def SnapMsg(self,msg): + self._MyList[0].SetText(msg) + self._MyList[0]._PosX = (self._Width - self._MyList[0]._Width)/2 + self._MyList[0]._PosY = (self._Height - self._MyList[0]._Height)/2 + + self._BGPosX = self._MyList[0]._PosX-10 + self._BGPosY = self._MyList[0]._PosY-10 + self._BGWidth = self._MyList[0]._Width+20 + self._BGHeight = self._MyList[0]._Height+20 + + def Init(self): + self._PosX = self._Index * self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + self._CanvasHWND = self._Screen._CanvasHWND + + ps = ListPageSelector() + ps._Parent = self + self._Ps = ps + self._PsIndex = 0 + + li = Label() + li.SetCanvasHWND(self._CanvasHWND) + li.Init(self._ConfirmText,self._ListFont) + + li._PosX = (self._Width - li._Width)/2 + li._PosY = (self._Height - li._Height)/2 + + self._BGPosX = li._PosX-10 + self._BGPosY = li._PosY-10 + self._BGWidth = li._Width+20 + self._BGHeight = li._Height+20 + + self._MyList.append(li) + + def KeyDown(self,event): + + if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]: + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def DrawBG(self): + _rect = pygame.Rect(self._BGPosX,self._BGPosY,self._BGWidth,self._BGHeight) + + pygame.draw.rect(self._CanvasHWND,(255,255,255),_rect,0) + pygame.draw.rect(self._CanvasHWND,(83,83,83),_rect,1) + + def Draw(self): + #self.ClearCanvas() + + self.DrawBG() + for i in self._MyList: + i.Draw() + + self.Reset() diff --git a/sys.py/UI/constants.py b/sys.py/UI/constants.py new file mode 100644 index 0000000..941110e --- /dev/null +++ b/sys.py/UI/constants.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + +from datetime import datetime + +import base64 +from beeprint import pp + + +Width = 320 +Height = 240 +bg_color = pygame.Color(255,255,255) + +icon_width = 80 +icon_height = 80 +icon_ext = ".sh" + + +ICON_TYPES={"Emulator":7,"FILE":6,"STAT":5,"NAV":4,"LETTER":3,"FUNC":2,"DIR":1,"EXE":0,"None":-1} # FUNC is like UI widget's function,DIR contains child page,EXE just execute a binary + +## H=horizontal ,V=vertical S=Single Line +#SLeft start from left, single line +#SCenter star from center ,single line +ALIGN = {"HLeft":0,"HCenter":1,"HRight":2,"VMiddle":3,"SLeft":4,"VCenter":5,"SCenter":6} + +DT = pygame.time.Clock().tick(30) # fps in ms,eg:50 + + +GMEVT = pygame.USEREVENT+1 +update_titlebar_event = pygame.event.Event(GMEVT, message="titlebar") + +RUNEVT = pygame.USEREVENT+2 +RUNSYS = pygame.USEREVENT+3 + + +LOWLIGHT = pygame.USEREVENT+4 ## when dim screen backlight + +FOOTMSG = pygame.USEREVENT+5 ## when dim screen backlight + + diff --git a/sys.py/UI/delete_confirm_page.py b/sys.py/UI/delete_confirm_page.py new file mode 100644 index 0000000..da76b82 --- /dev/null +++ b/sys.py/UI/delete_confirm_page.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +import pygame +import os +import shutil + + +#UI lib +from constants import Width,Height,ICON_TYPES +from page import Page,PageSelector +from label import Label +from fonts import fonts +from util_funcs import midRect +from keys_def import CurKeys +from confirm_page import ConfirmPage + +class DeleteConfirmPage(ConfirmPage): + + _FileName = "" + _TrashDir = "" + _ConfirmText = "Confirm delete?" + + def SetTrashDir(self,d): + self._TrashDir = d + + if os.path.isdir(self._TrashDir) == False: + raise IOError("Trash not existed") + + def SetFileName(self,fn): + self._FileName = fn + + def KeyDown(self,event): + + if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]: + + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + + if event.key == CurKeys["B"]: + try: + os.remove(self._TrashDir+"/"+os.path.basename(self._FileName)) + except: + pass + + try: + shutil.move(self._FileName, self._TrashDir) + except shutil.Error as e: + if "already exists" in str(e): + self._Screen._MsgBox.SetText("Already existed") + else: + self._Screen._MsgBox.SetText("Error ") + + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + else: + #self._Screen._MsgBox.SetText("Deleteing..") + #self._Screen._MsgBox.Draw() + #self._Screen.SwapAndShow() + self.SnapMsg("Deleteing....") + self._Screen.Draw() + self._Screen.SwapAndShow() + self.Reset() + + pygame.time.delay(300) + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + print(self._FileName) + + diff --git a/sys.py/UI/download.py b/sys.py/UI/download.py new file mode 100644 index 0000000..cb97470 --- /dev/null +++ b/sys.py/UI/download.py @@ -0,0 +1,213 @@ +import os +import pycurl +import sys +import time +import urllib2 +import hashlib + +from threading import Thread + +class Download(Thread): + _dst_path = "" + _is_finished = False + _is_successful = False + _errors = [] + _HashFunc = None + _HashValue = "" + + def __init__(self, url, path, cookies=False, useragent=False): + super(Download, self).__init__() + self.url = url + self.path = path + self.useragent = useragent + self.cookies = cookies + self.downloaded = 0 + + self.progress = { 'downloaded': 0, 'total': 0, 'percent': 0,'stopped':False } + self.stop = False + self.filename = "" + + def isFinished(self): + return self._is_finished + + def isSuccessful(self): + return self._is_successful + + def get_dest(self): + return self._dst_path + + def get_errors(self): + return self._errors + + def run(self): + + c = pycurl.Curl() + c.setopt(pycurl.URL, self.url) + c.setopt(pycurl.FOLLOWLOCATION, 1) + c.setopt(pycurl.MAXREDIRS, 5) + c.setopt(pycurl.NOBODY, 1) + + c.setopt(pycurl.CONNECTTIMEOUT, 10) + + if self.useragent: + c.setopt(pycurl.USERAGENT, self.useragent) + + # add cookies, if available + if self.cookies: + c.setopt(pycurl.COOKIE, self.cookies) + c.perform() + realurl = c.getinfo(pycurl.EFFECTIVE_URL) + + self.filename = realurl.split("/")[-1].strip() + + c = pycurl.Curl() + c.setopt(pycurl.CONNECTTIMEOUT, 10) + c.setopt(pycurl.URL, realurl) + c.setopt(pycurl.FOLLOWLOCATION, 0) + c.setopt(pycurl.NOPROGRESS, False) + c.setopt(pycurl.XFERINFOFUNCTION, self.getProgress) + if self.useragent: + c.setopt(pycurl.USERAGENT, self.useragent) + + # configure pycurl output file + if self.path == False: + self.path = os.getcwd() + filepath = os.path.join(self.path, self.filename) + + if os.path.exists(filepath):## remove old file,restart download + os.system("rm -rf " + filepath) + f = open(filepath, "wb") + else: + f = open(filepath, "wb") + + c.setopt(pycurl.WRITEDATA, f) + + self._dst_path = filepath + + # add cookies, if available + if self.cookies: + c.setopt(pycurl.COOKIE, self.cookies) + + # download file + try: + c.perform() + except pycurl.error, error: + errno,errstr = error + print("curl error: %s" % errstr) + self._errors.append(errstr) + self.stop = True + self.progress["stopped"] = True + finally: + + code = c.getinfo( c.RESPONSE_CODE ) + c.close() + self._is_finished = True + + if self.progress["percent"] < 100: + self._is_successful = False + else: + if self._HashFunc != None: + hashed = self.hashlib_hash(self._HashFunc, self._dst_path) + if hashed == self._HashValue: + self._is_successful= True + else: + self._is_successful = False + self._errors.append("hash failed") + else: + if code != 200: + self._is_successful = False + os.system("rm -rf " + self._dst_path) ## clear garbage file + self._errors.append("response error %d " % code) + else: + self._is_successful = True ## 100% downloaded without hash check + + def getProgress(self,download_t, download_d, upload_t, upload_d): + if download_t and download_d: + self.progress['downloaded'] = download_d + self.downloaded + self.progress['total'] = download_t + self.downloaded + self.progress['percent'] = ( float(self.progress['downloaded']) / float(self.progress['total'])) * 100.0 + self.progress["stopped"] = False + if self.stop: + self.progress["stopped"] = True + return 1 + + def hashlib_hash(method,fname): #eg: method == hashlib.md5(),function pointer + hash_ = method + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_.update(chunk) + + return hash_.hexdigest() + + def add_hash_verification(self,method_name,method_value): + if method_name == "md5": + self._HashFunc = hashlib.md5() + else: + self._HashFunc = None + + self._HashValue = method_value + + def get_progress(self): + return self.progress["percent"] + + def stop(self): + self.stop = True + + def cancel(self): + # sets the boolean to stop the thread. + self.stop = True + +def main(): + from optparse import OptionParser + + parser = OptionParser(usage="%prog [options] ") + parser.add_option( "-p", "--path", default=False, dest="path", help="download file to PATH", metavar="PATH") + parser.add_option( "-c", "--cookies", default=False, dest="cookies", help="specify cookie(s)", metavar="COOKIES") + opts, args = parser.parse_args() + + if len(args) == 0: + parser.error("No url supplied") + + for url in args: + print("Downloading: %s" % url) + if opts.path: + print("to: %s" % opts.path) + else: + print("to current directory") + d = Download(url, opts.path, opts.cookies) + d.start() + + last_downloaded = 0 + sleep_time = 0.05 + while 1: + try: + progress = d.progress['percent'] + + download_dx = d.progress["downloaded"] - last_downloaded + + speed = float(download_dx) / ( sleep_time * 1000.0) + + last_downloaded = d.progress["downloaded"] + + if d.progress["stopped"] == True: + break + + print("%.2f percent | %d of %d | %.1f KB/s" % (progress, d.progress['downloaded'], d.progress['total'], speed)) + + if progress == 100: + print("") + print("Download complete: %s" % d.filename) + break + time.sleep(sleep_time) + + # tell thread to terminate on keyboard interrupt, + # otherwise the process has to be killed manually + except KeyboardInterrupt: + d.cancel() + break + + except: + raise + +if __name__ == "__main__": + main() diff --git a/sys.py/UI/download_process_page.py b/sys.py/UI/download_process_page.py new file mode 100644 index 0000000..0aed599 --- /dev/null +++ b/sys.py/UI/download_process_page.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +import os +import pygame + +import gobject +import validators + + +from libs.roundrects import aa_round_rect + +## local UI import +from UI.constants import Width,Height,ICON_TYPES,RUNEVT +from UI.page import Page,PageSelector +from UI.label import Label +from UI.icon_item import IconItem +from UI.fonts import fonts +from UI.util_funcs import midRect,CmdClean,FileExists +from UI.keys_def import CurKeys +from UI.multi_icon_item import MultiIconItem +from UI.icon_pool import MyIconPool +from UI.download import Download + +from libs.DBUS import is_wifi_connected_now + +import config + +class DownloadProcessPage(Page): + _FootMsg = ["Nav.","","","Back",""] + _Downloader = None + _DownloaderTimer = -1 + _Value = 0 + + _URL = "" + _DST_DIR = "" + + _PngSize = {} + + _FileNameLabel = None + _SizeLabel = None + + _URLColor = pygame.Color(51,166,255) + _TextColor = pygame.Color(83,83,83) + + def __init__(self): + Page.__init__(self) + self._Icons = {} + self._CanvasHWND = None + + def Init(self): + self._PosX = self._Index * self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + self._CanvasHWND = self._Screen._CanvasHWND + + self._PngSize["bg"] = (48,79) + self._PngSize["needwifi_bg"] = (253,132) + + bgpng = IconItem() + bgpng._ImgSurf = MyIconPool._Icons["rom_download"] + bgpng._MyType = ICON_TYPES["STAT"] + bgpng._Parent = self + bgpng.Adjust(0,0,self._PngSize["bg"][0],self._PngSize["bg"][1],0) + self._Icons["bg"] = bgpng + + needwifi_bg = IconItem() + needwifi_bg._ImgSurf = MyIconPool._Icons["needwifi_bg"] + needwifi_bg._MyType = ICON_TYPES["STAT"] + needwifi_bg._Parent = self + needwifi_bg.Adjust(0,0,self._PngSize["needwifi_bg"][0],self._PngSize["needwifi_bg"][1],0) + + self._Icons["needwifi_bg"] = needwifi_bg + + + self._FileNameLabel = Label() + self._FileNameLabel.SetCanvasHWND(self._CanvasHWND) + self._FileNameLabel.Init("", fonts["varela12"]) + + self._SizeLabel = Label() + self._SizeLabel.SetCanvasHWND(self._CanvasHWND) + self._SizeLabel.Init("0/0Kb",fonts["varela12"]) + self._SizeLabel.SetColor( self._URLColor ) + + + def OnExitCb(self,event): + print("DownloadProcessPage OnExitCb") + if self._Downloader == None: + return + try: + self._Downloader.stop() + except: + pass + return + + def GObjectUpdateProcessInterval(self): + if self._Screen.CurPage() == self: + if self._Downloader.isFinished(): + if self._Downloader.isSuccessful(): + print("Success!") + # Do something with obj.get_dest() + filename = os.path.basename(self._Downloader.get_dest()) + cur_dir = os.getcwd() + + if filename.endswith(".zip"): + os.chdir(self._DST_DIR) + os.system( "unzip " + filename ) + + elif filename.endswith(".zsync"): + os.chdir(self._DST_DIR) + os.system( "rm -rf " + filename) + + elif filename.endswith(".tar.xz"): + os.chdir(self._DST_DIR) + os.system( "tar xf " + filename) + os.system( "rm -rf " + filename) + + os.chdir(cur_dir) + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + else: + print("Download failed with the following exceptions:") + for e in self._Downloader.get_errors(): + print(unicode(e)) + + try: + self._Downloader.stop() + except: + pass + + self._Screen._MsgBox.SetText("Download failed") + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + return False + else: + self._Value = self._Downloader.get_progress() + + filename = os.path.basename(self._Downloader.get_dest()) + self._FileNameLabel.SetText( filename ) + + downloaded = self._Downloader.progress["downloaded"] + total = self._Downloader.progress["total"] + + downloaded = downloaded/1000.0/1000.0 + total = total/1000.0/1000.0 + + self._SizeLabel.SetText( "%.2f" % downloaded+"/"+ "%.2f" % total +"Mb") + + print("Progress: %d%%" % (self._Value)) + self._Screen.Draw() + self._Screen.SwapAndShow() + return True + else: + return False + + def StartDownload(self,url,dst_dir): + if is_wifi_connected_now() == False: + return + + if validators.url(url) and os.path.isdir(dst_dir): + self._URL = url + self._DST_DIR = dst_dir + else: + self._Screen._MsgBox.SetText("Invaid") + self._Screen._MsgBox.Draw() + self._Screen.SwapAndShow() + print("url or dst dir error") + return + + self._Downloader = Download(url,dst_dir,None) + self._Downloader.start() + + self._DownloaderTimer = gobject.timeout_add(100, self.GObjectUpdateProcessInterval) + + def KeyDown(self,event): + if event.key == CurKeys["Menu"] or event.key == CurKeys["A"]: + gobject.source_remove(self._DownloaderTimer) + self._DownloaderTimer = -1 + + if self._Downloader != None: + try: + self._Downloader.stop() + except: + print("user canceled ") + + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def Draw(self): + self.ClearCanvas() + + if is_wifi_connected_now() == False: + self._Icons["needwifi_bg"].NewCoord(self._Width/2, self._Height/2) + self._Icons["needwifi_bg"].Draw() + return + + self._Icons["bg"].NewCoord(self._Width/2,self._Height/2-20) + self._Icons["bg"].Draw() + + percent = self._Value + if percent < 10: + percent = 10 + + + rect_ = midRect(self._Width/2,self._Height/2+33,170,17, Width,Height) + aa_round_rect(self._CanvasHWND,rect_, (238,238,238),5,0,(238,238,238)) + + rect2 = midRect(self._Width/2,self._Height/2+33,int(170*(percent/100.0)),17, Width,Height) + rect2.left = rect_.left + rect2.top = rect_.top + aa_round_rect(self._CanvasHWND,rect2, (126,206,244),5,0,(126,206,244)) + + rect3 = midRect(self._Width/2,self._Height/2+53,self._FileNameLabel._Width, self._FileNameLabel._Height,Width,Height) + + rect4 = midRect(self._Width/2,self._Height/2+70,self._SizeLabel._Width, self._SizeLabel._Height,Width,Height) + + self._FileNameLabel.NewCoord(rect3.left,rect3.top) + self._SizeLabel.NewCoord(rect4.left, rect4.top) + + self._FileNameLabel.Draw() + self._SizeLabel.Draw() + + diff --git a/sys.py/UI/fonts.py b/sys.py/UI/fonts.py new file mode 100644 index 0000000..fbcf3e4 --- /dev/null +++ b/sys.py/UI/fonts.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + +from datetime import datetime + + +if not pygame.font.get_init(): + pygame.font.init() + +fonts_path = {} + +fonts_path["varela"] = "../truetype/VarelaRound-Regular.ttf" +fonts_path["veramono"] = "../truetype/VeraMono.ttf" +fonts_path["noto"] = "../truetype/NotoSansMono-Regular.ttf" +fonts_path["notocjk"] = "../truetype/NotoSansCJK-Regular.ttf" + +fonts = {} +fonts["varela12"] = pygame.font.Font(fonts_path["varela"],12) +fonts["varela13"] = pygame.font.Font(fonts_path["varela"],13) +fonts["varela14"] = pygame.font.Font(fonts_path["varela"],14) +fonts["varela15"] = pygame.font.Font(fonts_path["varela"],15) + +fonts["varela16"] = pygame.font.Font(fonts_path["varela"],16) +fonts["varela18"] = pygame.font.Font(fonts_path["varela"],18) +fonts["varela20"] = pygame.font.Font(fonts_path["varela"],20) +fonts["varela22"] = pygame.font.Font(fonts_path["varela"],22) +fonts["varela23"] = pygame.font.Font(fonts_path["varela"],23) +fonts["varela24"] = pygame.font.Font(fonts_path["varela"],24) +fonts["varela25"] = pygame.font.Font(fonts_path["varela"],25) +fonts["varela26"] = pygame.font.Font(fonts_path["varela"],26) +fonts["varela27"] = pygame.font.Font(fonts_path["varela"],27) +fonts["varela28"] = pygame.font.Font(fonts_path["varela"],28) +fonts["varela34"] = pygame.font.Font(fonts_path["varela"],34) +fonts["varela40"] = pygame.font.Font(fonts_path["varela"],40) + +fonts["veramono25"] = pygame.font.Font(fonts_path["veramono"],25) +fonts["veramono24"] = pygame.font.Font(fonts_path["veramono"],24) +fonts["veramono23"] = pygame.font.Font(fonts_path["veramono"],23) +fonts["veramono22"] = pygame.font.Font(fonts_path["veramono"],22) +fonts["veramono21"] = pygame.font.Font(fonts_path["veramono"],21) +fonts["veramono20"] = pygame.font.Font(fonts_path["veramono"],20) +fonts["veramono18"] = pygame.font.Font(fonts_path["veramono"],18) +fonts["veramono16"] = pygame.font.Font(fonts_path["veramono"],16) +fonts["veramono15"] = pygame.font.Font(fonts_path["veramono"],15) +fonts["veramono14"] = pygame.font.Font(fonts_path["veramono"],14) +fonts["veramono13"] = pygame.font.Font(fonts_path["veramono"],13) +fonts["veramono12"] = pygame.font.Font(fonts_path["veramono"],12) +fonts["veramono11"] = pygame.font.Font(fonts_path["veramono"],11) +fonts["veramono10"] = pygame.font.Font(fonts_path["veramono"],10) + +for i in range(10,18): + fonts["notosansmono"+str(i)] = pygame.font.Font(fonts_path["noto"],i) + +for i in range(10,18): + fonts["notosanscjk"+str(i)] = pygame.font.Font(fonts_path["notocjk"],i) + +fonts["arial"] = pygame.font.SysFont("arial",16) + diff --git a/sys.py/UI/foot_bar.py b/sys.py/UI/foot_bar.py new file mode 100644 index 0000000..18e9e4e --- /dev/null +++ b/sys.py/UI/foot_bar.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +import pygame +import os + + +##local import +from constants import Width,Height,ICON_TYPES,ALIGN +from util_funcs import FileExists,midRect +from icon_item import IconItem +from fonts import fonts +from multi_icon_item import MultiIconItem + +from libs.roundrects import aa_round_rect + +icon_base_path = "gameshell/footbar_icons/" + +class FootBarIcon(MultiIconItem): + + def TotalWidth(self): + return self._Width+self._Label._Width + + def Draw(self): + if self._Align==ALIGN["VCenter"]: #default + if self._Label != None: + self._Label._PosX = self._PosX - self._Label._Width/2 + self._Label._PosY = self._PosY + self._Height/2 + 12 + + elif self._Align ==ALIGN["HLeft"]: + if self._Label != None: + self._Label._PosX = self._PosX + self._Width/2 + 3 + self._Label._PosY = self._PosY - self._Label._Height/2 + + if self._Label!=None: + self._Label.Draw() + + if self._ImgSurf != None: + self._Parent._CanvasHWND.blit(self._ImgSurf,midRect(self._PosX, + self._PosY, + self._Width,self._Height,Width,Height), + (0,self._IconIndex*self._IconHeight,self._IconWidth,self._IconHeight)) +class FootBar: + _PosX = 0 + _PosY = Height-20 + _Width = Width + _Height = 20 + _BarHeight = 20.5 + _BorderWidth = 1 + _CanvasHWND = None + _HWND = None + _Icons = {} + _IconWidth = 18 + _IconHeight = 18 + _LabelFont = fonts["veramono10"] + _State = "normal" + _BgColor = pygame.Color(255,255,255) + + def __init__(self): + self._Icons = {} + + + def ReadFootBarIcons(self,icondir): + if FileExists(icondir) == False and os.path.isdir(icondir) == False: + return + + keynames = ["nav","x","y","a","b"] + + share_surf = pygame.image.load(icon_base_path+"footbar.png").convert_alpha() + + files = os.listdir(icondir) + for _i,i in enumerate( keynames): + it = FootBarIcon() + it._MyType = ICON_TYPES["NAV"] + it._Parent = self + it._ImgSurf= share_surf + it._Align = ALIGN["HLeft"] # (x)text <= + + it.AddLabel("game",self._LabelFont) + it.Adjust(self._IconWidth/2+_i*self._IconWidth, self._IconHeight/2+2, self._IconWidth, self._IconHeight,0) + it._IconIndex = _i + self._Icons[i] = it + + + def Init(self,screen): + self._HWND = screen + self._CanvasHWND = pygame.Surface((Width,int(self._BarHeight))) + + self.ReadFootBarIcons(icon_base_path) + + def ResetNavText(self): + self._Icons["nav"]._Label.SetText("Nav.") + self._State = "normal" + self.Draw() + + def UpdateNavText(self,texts): + self._State = "tips" + my_text = self._LabelFont.render(texts,True,(83,83,83)) + """ + _w = 0 + for i, x in enumerate(("b","a","y","x")): + if self._Icons[x]._Label._Text!="": + if i==0: + _w += self._Icons[x].TotalWidth() + else: + _w += self._Icons[x].TotalWidth()+5 + """ + left_width = self._Width - 18 + + final_piece = "" + for i ,v in enumerate(texts): + text_slice = texts[:i+1] + my_text = self._LabelFont.render(text_slice,True,(83,83,83)) + final_piece = text_slice + if my_text.get_width() >= left_width: + break + + print("finalpiece %s" %final_piece) + self._Icons["nav"]._Label.SetText( final_piece ) + + self.Draw() + + def SetLabelTexts(self,texts): + for idx,x in enumerate(("nav","x","y","a","b")): + try: + self._Icons[x]._Label.SetText(texts[idx]) + except IndexError: + print("Index "+x+" doesn't exist!") + + + def ClearCanvas(self): + self._CanvasHWND.fill((0,0,0)) + aa_round_rect(self._CanvasHWND, + (0,0,self._Width,self._Height),self._BgColor,8,0, self._BgColor) + + pygame.draw.rect(self._CanvasHWND,self._BgColor,(0,0,Width,self._BarHeight/2), 0 ) + + def Draw(self): + self.ClearCanvas() + self._Icons["nav"].NewCoord(self._IconWidth/2+3,self._IconHeight/2+2) + self._Icons["nav"].Draw() + + if self._State == "normal": + _w=0 + #for i,x in enumerate(("a","b","x","y")): + for i, x in enumerate(("b","a","y","x")): + if self._Icons[x]._Label._Text!="": + if i==0: + _w += self._Icons[x].TotalWidth() + else: + _w += self._Icons[x].TotalWidth()+5 + + start_x = self._Width - _w + start_y = self._IconHeight/2+2 + self._Icons[x].NewCoord(start_x,start_y) + self._Icons[x].Draw() + + + pygame.draw.line(self._CanvasHWND,(169,169,169),(0,0),(Width,0),self._BorderWidth) + + if self._HWND != None: + self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,Width,self._BarHeight)) diff --git a/sys.py/UI/icon_item.py b/sys.py/UI/icon_item.py new file mode 100644 index 0000000..44b3ff2 --- /dev/null +++ b/sys.py/UI/icon_item.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +import pygame + +## local import +from constants import icon_width,icon_height,ICON_TYPES,ALIGN,icon_ext,Width,Height +from util_funcs import color_surface,midRect +from label import Label + +class IconItem: + _PosX=0 + _PosY=0 + _Width=0 + _Height=0 + _ImageName="" + _ImgSurf = None + _Parent = None + _Index = 0 + _MyType = ICON_TYPES["EXE"] + _CmdPath = "" + _LinkPage = None + _Label = None + _Align = ALIGN["VCenter"] # set for the Icon Image and Text Label + + def __init__(self): + self._ImgSurf=None + + + def Init(self,x,y,w,h,at): # the Surface is assigned in Screen + self._PosX = x + self._PosY = y + self._Width = w + self._Height = h + self._AnimationTime = at + + def SetLableColor(self,color): + self._Label.SetColor(color) + + def NewCoord(self,x,y): + self._PosX = x + self._PosY = y + + def AddLabel(self,text,fontobj): + if self._Label == None: + self._Label = Label() + self._Label.Init(text,fontobj) + else: + #just replace the text + self._Label._Init(text,fontobj) + + def Adjust(self,x,y,w,h,at): # the Surface is assigned in Screen + self.Init(x,y,w,h,at) + + if self._Label != None: + self._Label.SetCanvasHWND( self._Parent._CanvasHWND) + + self.CreateImageSurf() + self.AdjustLinkPage() + + def AdjustLinkPage(self): + if self._MyType==ICON_TYPES["DIR"] and self._LinkPage != None: + self._LinkPage._Index = 0 + self._LinkPage._Align = ALIGN["SLeft"] + self._LinkPage._IconNumbers = len(self._LinkPage._Icons) + self._LinkPage._Screen = self._Parent._Screen + self._LinkPage._CanvasHWND = self._Parent._Screen._CanvasHWND + + self._LinkPage._FootMsg = ["Nav.","","","Back","Enter"] ## Default Page Foot info + + if self._LinkPage._Align == ALIGN["HLeft"]: + self._LinkPage.AdjustHLeftAlign() + elif self._LinkPage._Align == ALIGN["SLeft"]: + self._LinkPage.AdjustSAutoLeftAlign() + if self._LinkPage._IconNumbers > 1: + self._LinkPage._PsIndex = 1 + self._LinkPage._IconIndex = self._LinkPage._PsIndex + + def CreateImageSurf(self): + + if self._ImgSurf == None and self._ImageName != "": +# print(self._ImageName) + self._ImgSurf = pygame.image.load( self._ImageName ).convert_alpha() + if self._ImgSurf.get_width() > icon_width or self._ImgSurf.get_height() > icon_height: + self._ImgSurf = pygame.transform.scale(self._ImgSurf,(icon_width,icon_height)) + + def ChangeImgSurfColor(self,color): + color_surface(self._ImgSurf,color) + + def Clear(self): + pass + + def Draw(self): + if self._Align==ALIGN["VCenter"]: #default + if self._Label != None: + self._Label._PosX = self._PosX - self._Label._Width/2 + self._Parent._PosX + self._Label._PosY = self._PosY + self._Height/2 +6 + self._Parent._PosY + + elif self._Align ==ALIGN["HLeft"]: + if self._Label != None: + self._Label._PosX = self._PosX + self._Width/2 + 3 + self._Parent._PosX + self._Label._PosY = self._PosY - self._Label._Height/2 + self._Parent._PosY + + if self._Label!=None: + self._Label.Draw() + + if self._ImgSurf != None: + self._Parent._CanvasHWND.blit(self._ImgSurf,midRect(self._PosX+self._Parent._PosX, + self._PosY+self._Parent._PosY, + self._Width,self._Height,Width,Height)) diff --git a/sys.py/UI/icon_pool.py b/sys.py/UI/icon_pool.py new file mode 100644 index 0000000..17b7412 --- /dev/null +++ b/sys.py/UI/icon_pool.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + + +##pool only store surfaces + +class IconPool(object): + + _GameShellIconPath = "gameshell/icons/" + _Icons = {} + def __init__(self): + self._Icons= {} + + def Init(self): + + files = os.listdir(self._GameShellIconPath) + for i in files: + if os.path.isfile(self._GameShellIconPath+"/"+i) and i.endswith(".png"): + keyname = i.split(".")[0] + self._Icons[keyname] = pygame.image.load(self._GameShellIconPath+"/"+i).convert_alpha() + + +MyIconPool = IconPool() + diff --git a/sys.py/UI/keyboard_keys.layout b/sys.py/UI/keyboard_keys.layout new file mode 100644 index 0000000..a77e2ff --- /dev/null +++ b/sys.py/UI/keyboard_keys.layout @@ -0,0 +1,14 @@ +1 2 3 4 5 6 7 8 9 0 +q w e r t y u i o p +a s d f g h j k l +_L z x c v b n m _R + +1 2 3 4 5 6 7 8 9 0 +Q W E R T Y U I O P +A S D F G H J K L +_L Z X C V B N M _R + +! @ # $ % ^ & * ( ) +- _ + = ~ ` [ ] { } +| \ : ; " ' < > , . +_L ? / _R _S diff --git a/sys.py/UI/keys_def.py b/sys.py/UI/keys_def.py new file mode 100644 index 0000000..20f172e --- /dev/null +++ b/sys.py/UI/keys_def.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + + +from config import CurKeySet + +GameShell = {} +""" +GameShell["Up"] = pygame.K_w +GameShell["Down"] = pygame.K_s +GameShell["Left"] = pygame.K_a +GameShell["Right"]= pygame.K_d +""" +GameShell["Up"] = pygame.K_UP +GameShell["Down"] = pygame.K_DOWN +GameShell["Left"] = pygame.K_LEFT +GameShell["Right"]= pygame.K_RIGHT + +GameShell["Menu"] = pygame.K_ESCAPE +GameShell["X"] = pygame.K_u +GameShell["Y"] = pygame.K_i +GameShell["A"] = pygame.K_j +GameShell["B"] = pygame.K_k + +GameShell["Vol-"] = pygame.K_SPACE +GameShell["Vol+"] = pygame.K_RETURN +GameShell["Space"] = pygame.K_SPACE + +GameShell["Enter"] = pygame.K_k +GameShell["Start"] = pygame.K_RETURN + + +PC = {} + +PC["Up"] = pygame.K_UP +PC["Down"] = pygame.K_DOWN +PC["Left"] = pygame.K_LEFT +PC["Right"] = pygame.K_RIGHT +PC["Menu"] = pygame.K_ESCAPE +PC["X"] = pygame.K_x +PC["Y"] = pygame.K_y +PC["A"] = pygame.K_a +PC["B"] = pygame.K_b +PC["Vol-"] = pygame.K_SPACE +PC["Vol+"] = pygame.K_RETURN +PC["Enter"] = pygame.K_RETURN +PC["Space"] = pygame.K_SPACE +PC["Start"] = pygame.K_s + +if CurKeySet == "PC": + CurKeys = PC +else: + CurKeys = GameShell diff --git a/sys.py/UI/label.py b/sys.py/UI/label.py new file mode 100644 index 0000000..e84deea --- /dev/null +++ b/sys.py/UI/label.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import pygame + +#import base64 +#from beeprint import pp + +class Label: + _PosX=0 + _PosY=0 + _Width=0 + _Height=0 + _Text="" + _FontObj=None + _Color = pygame.Color(83,83,83) + _CanvasHWND = None + _TextSurf = None + def __init__(self): + pass + + def Init(self,text,font_obj,color=pygame.Color(83,83,83)): + self._Color = color + self._FontObj = font_obj + self._Text = text + + my_text = self._FontObj.render(self._Text,True,self._Color) + self._Width = my_text.get_width() + self._Height = my_text.get_height() + + def NewCoord(self,x,y): + self._PosX = x + self._PosY = y + + def SetColor(self,color): + self._Color = color + + def GetText(self): + return self._Text + + def SetText(self,text): + self._Text = text + + my_text = self._FontObj.render(self._Text,True,self._Color) + self._Width = my_text.get_width() + self._Height = my_text.get_height() + + def Width(self): + return self._Width + + def SetCanvasHWND(self,_canvashwnd): + self._CanvasHWND = _canvashwnd + + def Draw(self): + my_text = self._FontObj.render( self._Text,True,self._Color) + + self._CanvasHWND.blit(my_text,(self._PosX,self._PosY,self._Width,self._Height)) diff --git a/sys.py/UI/main_screen.py b/sys.py/UI/main_screen.py new file mode 100644 index 0000000..c01453f --- /dev/null +++ b/sys.py/UI/main_screen.py @@ -0,0 +1,468 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + +from libs import easing +from datetime import datetime + +import base64 +from beeprint import pp + +## local package import +from constants import ICON_TYPES,icon_ext,icon_width,icon_height,RUNEVT +from icon_item import IconItem +from page import Page,PageStack +from title_bar import TitleBar +from foot_bar import FootBar +from constants import Width,Height,bg_color +from util_funcs import FileExists,ReplaceSuffix,ReadTheFileContent,CmdClean,MakeExecutable +from fonts import fonts +from keys_def import CurKeys +from label import Label +from untitled_icon import UntitledIcon +from Emulator import MyEmulator + +class MessageBox(Label): + _Parent = None + + def Draw(self): + my_text = self._FontObj.render( self._Text,True,self._Color) + w = my_text.get_width() + h = my_text.get_height() + x = (self._Parent._Width - w)/2 + y = (self._Parent._Height - h)/2 + + padding = 10 + pygame.draw.rect(self._CanvasHWND,(255,255,255),(x-padding,y-padding, w+padding*2,h+padding*2)) + + pygame.draw.rect(self._CanvasHWND,(0,0,0),(x-padding,y-padding, w+padding*2,h+padding*2),1) + + self._CanvasHWND.blit(my_text,(x,y,w,h)) + + +python_package_flag = "__init__.py" +emulator_flag = "action.config" + +##Abstract object for manage Pages ,not the pygame's physic screen +class MainScreen(object): + _Pages = [] + _PageMax = 0 + _PageIndex = 0 + _PosX = 0 + _PosY = TitleBar._BarHeight+1 + _Width = Width + _Height = Height -FootBar._BarHeight -TitleBar._BarHeight-1 + _MyPageStack = None + _CurrentPage = None # pointer to the current displaying Page Class + _CanvasHWND = None + _HWND = None + _TitleBar = None + _FootBar = None + _MsgBox = None + _MsgBoxFont = fonts["veramono20"] + _IconFont = fonts["varela15"] + + def __init__(self): + self._Pages = [] + self._MyPageStack = PageStack() + + def Init(self): + self._CanvasHWND = pygame.Surface((self._Width,self._Height)) + self._MsgBox= MessageBox() + self._MsgBox._Parent= self + self._MsgBox.SetCanvasHWND(self._CanvasHWND) + self._MsgBox.Init(" ", self._MsgBoxFont) + + def FartherPages(self): + self._PageMax = len(self._Pages) + for i in range(0,self._PageMax): + self._Pages[i]._Index = i + self._Pages[i]._CanvasHWND = self._CanvasHWND + self._Pages[i]._IconNumbers = len(self._Pages[i]._Icons) + self._Pages[i]._Screen = self + self._Pages[i].Adjust() + + if self._Pages[i]._IconNumbers > 1: + self._Pages[i]._PsIndex = 1 + self._Pages[i]._IconIndex = self._Pages[i]._PsIndex + + + self._CurrentPage = self._Pages[self._PageIndex] + self._CurrentPage._OnShow = True + + def GetMyRightSidePage(self): + ret = self._PageIndex +1 + if ret > (self._PageMax -1): + ret = self._PageMax -1 + return ret + + def PageMoveLeft(self): + self._Pages[self._PageIndex]._OnShow = False + if self._PageIndex < (self._PageMax - 1): + my_right_side_page = self.GetMyRightSidePage() + for i in range(0,self._PageMax): + if i!= self._PageIndex and i != my_right_side_page: + self._Pages[i].MoveLeft(Width) + + self._Pages[self._PageIndex].EasingLeft(Width) + + if self._PageIndex != my_right_side_page: + self._Pages[my_right_side_page].EasingLeft(Width) + + self._Pages[self._PageIndex].ResetPageSelector() + + self._PageIndex+=1 + if self._PageIndex > (self._PageMax -1): + self._PageIndex = (self._PageMax -1) + + self._Pages[self._PageIndex]._OnShow = True + self._CurrentPage = self._Pages[self._PageIndex] + + def GetMyLeftSidePage(self): + ret = self._PageIndex -1 + if ret < 0: + ret = 0 + return ret + + def PageMoveRight(self): + self._Pages[self._PageIndex]._OnShow = False + if self._PageIndex > 0: + my_left_side_page = self.GetMyLeftSidePage() + for i in range(0,self._PageMax): + if i!= self._PageIndex and i!= my_left_side_page: + pass + #self._Pages[i].MoveRight(Width) + + self._Pages[self._PageIndex].EasingRight(Width) + + if self._PageIndex != my_left_side_page: + self._Pages[my_left_side_page].EasingRight(Width) + + self._Pages[self._PageIndex].ResetPageSelector() + + self._PageIndex-=1 + if self._PageIndex < 0: + self._PageIndex = 0 + + self._Pages[self._PageIndex]._OnShow = True + self._CurrentPage = self._Pages[self._PageIndex] + + + def EasingAllPageLeft(self): + current_time = 0.0 + start_posx = 0.0 + current_posx = start_posx + final_posx = float(Width) + posx_init = 0 + dur = 30 + last_posx = 0.0 + all_last_posx = [] + if self._PageIndex >= (self._PageMax - 1): + return + for i in range(0,Width*dur): + current_posx = easing.SineIn(current_time,start_posx,final_posx-start_posx,float(dur)) + if current_posx >= final_posx: + current_posx = final_posx + + dx = current_posx - last_posx + all_last_posx.append(int(dx)) + current_time+=1 + last_posx = current_posx + if current_posx >= final_posx: + break + + c = 0 + for i in all_last_posx: + c+=i + if c < final_posx - start_posx: + all_last_posx.append( final_posx - c ) + + for i in all_last_posx: + self.ClearCanvas() + for j in self._Pages: + j._PosX -= i + j.DrawIcons() + j._Screen.SwapAndShow() + + + self._Pages[self._PageIndex]._OnShow = False + + self._PageIndex+=1 + if self._PageIndex > (self._PageMax -1): + self._PageIndex = (self._PageMax -1) + + self._Pages[self._PageIndex]._OnShow = True + self._CurrentPage = self._Pages[self._PageIndex] + + def EasingAllPageRight(self): + current_time = 0.0 + start_posx = 0.0 + current_posx = start_posx + final_posx = float(Width) + posx_init = 0 + dur = 30 + last_posx = 0.0 + all_last_posx = [] + if self._PageIndex <= 0: + return + for i in range(0,Width*dur): + current_posx = easing.SineIn(current_time,start_posx,final_posx-start_posx,float(dur)) + if current_posx >= final_posx: + current_posx = final_posx + + dx = current_posx - last_posx + all_last_posx.append(int(dx)) + current_time+=1 + last_posx = current_posx + if current_posx >= final_posx: + break + + c = 0 + for i in all_last_posx: + c+=i + if c < final_posx - start_posx: + all_last_posx.append( final_posx - c ) + + for i in all_last_posx: + self.ClearCanvas() + for j in reversed(self._Pages): + j._PosX += i + j.DrawIcons() + j._Screen.SwapAndShow() + + + self._Pages[self._PageIndex]._OnShow = False + + self._PageIndex-=1 + if self._PageIndex < 0: + self._PageIndex = 0 + + self._Pages[self._PageIndex]._OnShow = True + self._CurrentPage = self._Pages[self._PageIndex] + + def CurPage(self): + return self._CurrentPage + + def PushCurPage(self): + self._MyPageStack.Push(self._CurrentPage) + + def SetCurPage(self,page): + self._CurrentPage = page + on_load_cb = getattr(self._CurrentPage,"OnLoadCb",None) + if on_load_cb != None: + if callable( on_load_cb ): + self._CurrentPage.OnLoadCb() + + def PushPage(self,page): + self.PushCurPage() + self.SetCurPage(page) + + def AppendPage(self,Page): + self._Pages.append(Page) + + def ClearCanvas(self): + self._CanvasHWND.fill((255,255,255)) + + def SwapAndShow(self): + if self._HWND != None: + self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,self._Width,self._Height)) + pygame.display.update() + + def ExtraName(self,name): + ## extra name like 1_xxx to be => xxx, + parts = name.split("_") + if len(parts) > 1: + return parts[1] + elif len(parts) == 1: + return parts[0] + else: + return name + + def IsEmulatorPackage(self,dirname): + files = os.listdir(dirname) + for i in sorted(files): + if i.endswith(emulator_flag): + return True + return False + + def IsPythonPackage(self,dirname): + files = os.listdir(dirname) + for i in sorted(files): + if i.endswith(python_package_flag): + return True + return False + + def ReadTheDirIntoPages(self,_dir,pglevel,cur_page): + + if FileExists(_dir) == False and os.path.isdir(_dir) == False: + return + + files = os.listdir(_dir) + for i in sorted(files): + if os.path.isdir(_dir+"/"+i): # TOPLEVEL only is dir + if pglevel == 0: + page = Page() + page._Name = self.ExtraName(i) + page._Icons = [] + self._Pages.append(page) + self.ReadTheDirIntoPages(_dir+"/"+i, pglevel+1 ,self._Pages[ len(self._Pages) -1]) + else: ## On CurPage now + i2 = self.ExtraName(i) + iconitem = IconItem() + iconitem._CmdPath = "" + iconitem.AddLabel(i2,self._IconFont) + if FileExists(_dir+"/"+i2+".png"): + iconitem._ImageName = _dir+"/"+i2+".png" + else: + untitled = UntitledIcon() + untitled.Init() + if len(i2) > 1: + untitled.SetWords(i2[:2]) + elif len(i2) == 1: + untitled.SetWords([i2[0],i2[0]]) + else: + untitled.SetWords(["G","s"]) + + iconitem._ImgSurf = untitled.Surface() + iconitem._ImageName = "" + + if self.IsPythonPackage(_dir+"/"+i): + iconitem._MyType = ICON_TYPES["FUNC"] + sys.path.append(_dir) + iconitem._CmdPath = __import__(i) + init_cb = getattr(iconitem._CmdPath,"Init",None) + if init_cb != None: + if callable(init_cb): + iconitem._CmdPath.Init(self) + cur_page._Icons.append(iconitem) + + elif self.IsEmulatorPackage(_dir+"/"+i): + obj = {} + obj["ROM"] = "" + obj["ROM_SO"] ="" + obj["EXT"] = [] + obj["LAUNCHER"] = "" + obj["TITLE"] = "Game" + obj["SO_URL"] = "" + + try: + f = open(_dir+"/"+i+"/"+emulator_flag) + except IOError: + print("action config open failed") + return + else: + with f: + content = f.readlines() + content = [x.strip() for x in content] + for i in content: + pis = i.split("=") + if len(pis) > 1: + if "EXT" in pis[0]: + obj[pis[0]] = pis[1].split(",") + else: + obj[pis[0]] = pis[1] + + em = MyEmulator() + em._Emulator = obj + + em.Init(self) + iconitem._CmdPath = em + iconitem._MyType = ICON_TYPES["Emulator"] + cur_page._Icons.append(iconitem) + + else: + iconitem._MyType = ICON_TYPES["DIR"] + iconitem._LinkPage = Page() + iconitem._LinkPage._Name = i2 + cur_page._Icons.append(iconitem) + self.ReadTheDirIntoPages(_dir+"/"+i,pglevel+1,iconitem._LinkPage) + + elif os.path.isfile(_dir+"/"+i) and pglevel > 0: + if i.lower().endswith(icon_ext): + i2 = self.ExtraName(i) + + #cmd = ReadTheFileContent(_dir+"/"+i) + iconitem = IconItem() + iconitem._CmdPath = _dir+"/"+i + MakeExecutable(iconitem._CmdPath) + iconitem._MyType = ICON_TYPES["EXE"] + if FileExists(_dir+"/"+ReplaceSuffix(i2,"png")): + iconitem._ImageName = _dir+"/"+ReplaceSuffix(i2,"png") + else: + untitled = UntitledIcon() + untitled.Init() + if len(i2) > 1: + untitled.SetWords(i2[:2]) + elif len(i2) == 1: + untitled.SetWords([i2[0],i2[0]]) + else: + untitled.SetWords(["G","s"]) + + iconitem._ImgSurf = untitled.Surface() + + iconitem._ImageName = "" + + iconitem.AddLabel(i2.split(".")[0],self._IconFont) + iconitem._LinkPage = None + cur_page._Icons.append(iconitem) + + def RunEXE(self,cmdpath): + self.DrawRun() + self.SwapAndShow() + pygame.time.delay(1000) + cmdpath = cmdpath.strip() + cmdpath = CmdClean(cmdpath) + + pygame.event.post( pygame.event.Event(RUNEVT, message=cmdpath)) + + def OnExitCb(self,event): + ## leave rest to Pages + on_exit_cb = getattr(self._CurrentPage,"OnExitCb",None) + if on_exit_cb != None: + if callable( on_exit_cb ): + self._CurrentPage.OnExitCb(event) + return + + def KeyDown(self,event): + """ + if event.key == pygame.K_PAGEUP: + self.EasingAllPageLeft() + #self.SwapAndShow() + if event.key == pygame.K_PAGEDOWN: + self.EasingAllPageRight() + #self.SwapAndShow() + """ + if event.key == pygame.K_t: + self.DrawRun() + self.SwapAndShow() + + if event.key == CurKeys["Space"]: + self.Draw() + self.SwapAndShow() + + ## leave rest to Pages + current_page_key_down_cb = getattr(self._CurrentPage,"KeyDown",None) + if current_page_key_down_cb != None: + if callable( current_page_key_down_cb ): + self._CurrentPage.KeyDown(event) + + + def DrawRun(self): + self._MsgBox.SetText("Launching....") + self._MsgBox.Draw() + + def Draw(self): + self._CurrentPage.Draw() + #if self._HWND != None: + # self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,self._Width,self._Height)) + if self._TitleBar != None: + self._TitleBar.Draw(self._CurrentPage._Name) + if self._FootBar != None: + if hasattr(self._CurrentPage,"_FootMsg"): + self._FootBar.SetLabelTexts(self._CurrentPage._FootMsg) + self._FootBar.Draw() + diff --git a/sys.py/UI/multi_icon_item.py b/sys.py/UI/multi_icon_item.py new file mode 100644 index 0000000..4690aca --- /dev/null +++ b/sys.py/UI/multi_icon_item.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import pygame +#from beeprint import pp + +## local import +from constants import icon_width,icon_height,ICON_TYPES,ALIGN,icon_ext,Width,Height +from util_funcs import color_surface,midRect +from label import Label +from icon_item import IconItem + +##Resource file contains multi icons in single image +##usually the Image contains icons in Vertical, convert 1.png 2.png 3.png -append out.png + +class MultiIconItem(IconItem): + _IconWidth=18 + _IconHeight=18 + _IconIndex = 0 # icon index on the resource Image + + def CreateImageSurf(self): + if self._ImgSurf == None and self._ImageName != "": +# print(self._ImageName) + self._ImgSurf = pygame.image.load( self._ImageName ).convert_alpha() + + + def Draw(self): + if self._Align==ALIGN["VCenter"]: #default + if self._Label != None: + self._Label._PosX = self._PosX - self._Label._Width/2 + self._Parent._PosX + self._Label._PosY = self._PosY + self._Height/2 +6 + self._Parent._PosY + + elif self._Align ==ALIGN["HLeft"]: + if self._Label != None: + self._Label._PosX = self._PosX + self._Width/2 + 3 + self._Parent._PosX + self._Label._PosY = self._PosY - self._Label._Height/2 + self._Parent._PosY + + if self._Label!=None: + self._Label.Draw() + + if self._ImgSurf != None: + self._Parent._CanvasHWND.blit(self._ImgSurf,midRect(self._PosX+self._Parent._PosX, + self._PosY+self._Parent._PosY, + self._Width,self._Height,Width,Height), + (0,self._IconIndex*self._IconHeight,self._IconWidth,self._IconHeight)) diff --git a/sys.py/UI/multilabel.py b/sys.py/UI/multilabel.py new file mode 100644 index 0000000..661408e --- /dev/null +++ b/sys.py/UI/multilabel.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +import pygame + + +class MultiLabel: ##Multi Line Label + _PosX=0 + _PosY=0 + _Width=135 + _Height=100 + _Text="" + _FontObj=None + _Color = pygame.Color(83,83,83) + _CanvasHWND = None + _TextSurf = None + _MaxWidth = 0 + + def __init__(self): + pass + + def Init(self,text,font_obj,color=pygame.Color(83,83,83)): + self._Color = color + self._FontObj = font_obj + self._Text = text + + self.blit_text(self._CanvasHWND,self._Text,(self._PosX,self._PosY),self._FontObj) + + def NewCoord(self,x,y): + self._PosX = x + self._PosY = y + + def SetColor(self,color): + self._Color = color + + def GetText(self): + return self._Text + + def SetText(self,text): + self._Text = text + + self.blit_text(text) + + def Width(self): + return self._Width + + def SetCanvasHWND(self,_canvashwnd): + self._CanvasHWND = _canvashwnd + + def blit_text(self, surface,text, pos, font): + color = self._Color + words = [word.split(' ') for word in text.splitlines()] + space = font.size(' ')[0] + max_width = self._Width + x ,y = pos + row_total_width = 0 + lines = 0 + for i,line in enumerate(words[:4]): + for word in line[:12]: + #print(word) + word_surface = font.render(word, True, color) + word_width = word_surface.get_width() + word_height = word_surface.get_height() + row_total_width += word_width + if row_total_width+space >= max_width: + x = pos[0] # Reset the x. + y += word_height # Start on new row. + row_total_width = 0 + + if lines == 0: + lines += word_height + else: + lines += word_height + + surface.blit(word_surface, (x, y)) + x += word_width + space + x = pos[0] # Reset the x. + y += word_height # Start on new row. + lines += word_height + self._Height = lines + + + def Draw(self): + #my_text = self._FontObj.render( self._Text,True,self._Color) + self.blit_text(self._CanvasHWND,self._Text,(self._PosX,self._PosY),self._FontObj) + #pygame.draw.rect(self._CanvasHWND,(83,83,83), (self._PosX,self._PosY,self._Width,self._Height) , 1 ) + #self._CanvasHWND.blit(my_text,(self._PosX,self._PosY,self._Width,self._Height)) diff --git a/sys.py/UI/page.py b/sys.py/UI/page.py new file mode 100644 index 0000000..018a805 --- /dev/null +++ b/sys.py/UI/page.py @@ -0,0 +1,606 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys +import math + +from libs import easing + +import base64 +from beeprint import pp + +### local import +from constants import ALIGN,icon_width,icon_height,Width,Height,ICON_TYPES +from util_funcs import midRect +from keys_def import CurKeys + +from blueselector_b64 import blueselector + + +blueselector_surf = pygame.image.frombuffer(base64.b64decode(blueselector),(92,92),"RGBA") + +class PageStack: + def __init__(self): + self.stack = list() + + def Push(self,data): + if data not in self.stack: + self.stack.append(data) + return True + return False + + def Pop(self): + if len(self.stack)<=0: + return None,False + return self.stack.pop(),True + + def Length(self): + return len(self.stack) + +class PageSelector: + _PosX = 0 + _PosY = 0 + _Width = 0 + _Height = 0 + _Parent = None + _Alpha = 0 + _OnShow = True + _IconSurf = None + def __init__(self): + pass + + def Init(self,x,y,w,h,alpha): + self._PosX = x + self._PosY = y + self._Width = w + self._Height = h + self._Alpha = alpha + + def Adjust(self,x,y,w,h,alpha): + self._PosX = x + self._PosY = y + self._Width = w + self._Height = h + self._Alpha = alpha + + def Draw(self): + canvas = self._Parent._CanvasHWND + idx = self._Parent._PsIndex + iconidx = self._Parent._IconIndex + + if idx < len(self._Parent._Icons): + x = self._Parent._Icons[idx]._PosX+self._Parent._PosX + y = self._Parent._Icons[iconidx]._PosY ## only use current icon's PosY + + rect = midRect(x,y,self._Width,self._Height,self._Parent._Width,self._Parent._Height) + if rect.width <=0 or rect.height <= 0 : + return + + #color = (244,197,66,50) + #pygame.draw.rect(canvas,color,rect,1) + if self._IconSurf != None: + self._Parent._CanvasHWND.blit(self._IconSurf,rect) + +class Page(object): + _PosX=0 + _PosY=0 + _Width=0 + _Height=0 + _Icons = [] + _Ps = None + _PsIndex = 0 + _IconNumbers = 0 + _IconIndex = 0 ## shows which icon current selected, the Selector can not move here + _PrevIconIndex = 0 ## for remember the Highlighted Icon ,restore it's PosY to average + _Index = 0 + _Align = ALIGN["SLeft"] + _CanvasHWND = None # + _HWND = None # + _OnShow = False + _Name = "" + _Screen = None ## Should be the Screen Class + _PageIconMargin = 20 + _FootMsg = ["Nav.","","","","Enter"] ## Default Page Foot info + + _SelectedIconTopOffset=20 + _EasingDur = 30 + def __init__(self): + self._Icons = [] + + + def AdjustHLeftAlign(self): ## adjust coordinator and append the PageSelector + self._PosX = self._Index*self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + cols = int(Width /icon_width) + rows = int( (self._IconNumbers * icon_width)/Width + 1) + if rows < 1: + rows = 1 + cnt = 0 + for i in range(0,rows): + for j in range(0,cols): + start_x = icon_width/2 + j*icon_width + start_y = icon_height/2 + i*icon_height + icon = self._Icons[cnt] + icon.Adjust(start_x,start_y,icon_width-4,icon_height-4,0) + icon._Index = cnt + icon._Parent = self + if cnt >= (self._IconNumbers -1): + break + cnt+=1 + #icon的实际surface是要另行赋值的,这儿只创建空的icon,输入了坐标等信息 + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(icon_width/2, TitleBar._BarHeight+icon_height/2,92,92,128) + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + + def AdjustSLeftAlign(self): ## adjust coordinator and append the PageSelector + self._PosX = self._Index*self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + start_x = (self._PageIconMargin + icon_width+self._PageIconMargin) /2 + start_y = self._Height/2 + + for i in range(0,self._IconNumbers): + it = self._Icons[i] + it._Parent = self + it._Index = i + it.Adjust(start_x+i*self._PageIconMargin+i*icon_width,start_y,icon_width-6,icon_height-6,0) + it._ImgSurf = pygame.transform.smoothscale(it._ImgSurf,(it._Width,it._Height)) + + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(start_x,start_y,92,92,128) + + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + if self._IconNumbers > 1: + self._PsIndex = 1 + self._IconIndex = self._PsIndex + self._PrevIconIndex = self._IconIndex + self._Icons[self._IconIndex]._PosY -= self._SelectedIconTopOffset + + def AdjustSAutoLeftAlign(self): ## adjust coordinator and append the PageSelector + self._PosX = self._Index*self._Screen._Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + start_x = (self._PageIconMargin + icon_width+self._PageIconMargin) /2 + start_y = self._Height/2 + + if self._IconNumbers == 1: + start_x = self._Width / 2 + start_y = self._Height/2 + + it = self._Icons[0] + it._Parent = self + it._Index = 0 + it.Adjust(start_x,start_y,icon_width-6,icon_height-6,0) + it._ImgSurf = pygame.transform.smoothscale(it._ImgSurf,(it._Width,it._Height)) + + elif self._IconNumbers == 2: + start_x = (self._Width - self._PageIconMargin - self._IconNumbers*icon_width) / 2 + icon_width/2 + start_y = self._Height /2 + + for i in range(0,self._IconNumbers): + it = self._Icons[i] + it._Parent = self + it._Index = i + it.Adjust(start_x+i*self._PageIconMargin + i*icon_width,start_y, icon_width-6, icon_height-6,0) + it._ImgSurf = pygame.transform.smoothscale(it._ImgSurf,(it._Width,it._Height)) + + elif self._IconNumbers > 2: + for i in range(0,self._IconNumbers): + it = self._Icons[i] + it._Parent = self + it._Index = i + it.Adjust(start_x+i*self._PageIconMargin + i*icon_width,start_y,icon_width-6,icon_height-6,0) + + it._ImgSurf = pygame.transform.smoothscale(it._ImgSurf,(it._Width,it._Height)) + + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(start_x,start_y,92,92,128) + + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + if self._IconNumbers > 1: + self._PsIndex = 1 + self._IconIndex = self._PsIndex + self._PrevIconIndex = self._IconIndex + self._Icons[self._IconIndex]._PosY -= self._SelectedIconTopOffset + + def InitLeftAlign(self): + self._PosX = self._Index*Width + self._Width = self._Screen._Width + self._Height = self._Screen._Height + + cols = int(self._Width /icon_width) + rows = int((self._IconNumbers * icon_width)/self._Width + 1) + if rows < 1: + rows = 1 + cnt = 0 + for i in range(0,rows): + for j in range(0,cols): + start_x = icon_width/2 + j*icon_width + start_y = TitleBar._BarHeight + icon_height/2 + i*icon_height + icon = IconItem() + icon.Init(start_x,start_y,icon_width-4,icon_height-4,0) + icon._Index = cnt + icon._Parent = self + self._Icons.append(icon) + if cnt >= (self._IconNumbers -1): + break + cnt+=1 + #icon的实际surface是要另行赋值的,这儿只创建空的icon,输入了坐标等信息 + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(icon_width/2,icon_height/2,92,92,128) + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + def Adjust(self): ## default init way, + self._PosX = self._Index*self._Screen._Width + self._Width = self._Screen._Width ## equal to screen width + self._Height = self._Screen._Height + + if self._Align == ALIGN["HLeft"]: + start_x = (self._Width - self._IconNumbers*icon_width)/2 + icon_width/2 + start_y = self._Height/2 + + for i in range(0,self._IconNumbers): + it = self._Icons[i] + it._Parent = self + it._Index = i + it.Adjust(start_x+i*icon_width,start_y,icon_width,icon_height,0) + ps = PageSelector() + ps._IconSurf = blueselector_surf + + ps._Parent = self + ps.Init(start_x,start_y,92,92,128) + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + elif self._Align == ALIGN["SLeft"]: + start_x = (self._PageIconMargin + icon_width+self._PageIconMargin) /2 + start_y = self._Height/2 + + for i in range(0,self._IconNumbers): + it = self._Icons[i] + it._Parent = self + it._Index = i + it.Adjust(start_x+i*self._PageIconMargin+i*icon_width,start_y,icon_width,icon_height,0) + + + + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(start_x,start_y-self._SelectedIconTopOffset,92,92,128) + + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + if self._IconNumbers > 1: + self._PsIndex = 1 + self._IconIndex = self._PsIndex + self._PrevIconIndex = self._IconIndex + self._Icons[self._IconIndex]._PosY -= self._SelectedIconTopOffset + + + def Init(self): ## default init way, + if self._Screen != None: + if self._Screen._CanvasHWND != None and self._CanvasHWND == None: + self._CanvasHWND = self._Screen._CanvasHWND + + self._PosX = self._Index*self._Screen._Width + self._Width = self._Screen._Width ## equal to screen width + self._Height = self._Screen._Height + + start_x = (self._Width - self._IconNumbers*icon_width)/2 + icon_width/2 + start_y = self._Height/2 + + for i in range(0,self._IconNumbers): + it = IconItem() + it._Parent = self + it._Index = i + it.Init(start_x+i*icon_width,start_y,icon_width,icon_height,0) + self._Icons.append(it) + #icon的实际surface是要另行赋值的,这儿只创建空的icon,输入了坐标等信息 + if self._IconNumbers > 0: + ps = PageSelector() + ps._IconSurf = blueselector_surf + ps._Parent = self + ps.Init(start_x,start_y,icon_width+4,icon_height+4,128) + self._Ps = ps + self._PsIndex = 0 + self._OnShow = False + + def IconStepMoveData(self,icon_eh,cuts):## no Sine,No curve,plain movement steps data + all_pieces = [] + piece = icon_eh / cuts + + c = 0.0 + prev = 0.0 + for i in range(0,cuts): + c+=piece + dx = c-prev + if dx < 0.5: + dx = 1.0 + + all_pieces.append( math.ceil(dx) ) + if c >= icon_eh: + break + + c = 0 + bidx = 0 + for i in all_pieces: + c+=i + bidx+=1 + if c >= icon_eh: + break + + all_pieces = all_pieces[0:bidx] + + if len(all_pieces) < cuts: + dff = cuts - len(all_pieces) + diffa = [] + for i in range(0,dff): + diffa.append(0) + all_pieces.extend( diffa) + + return all_pieces + + def EasingData(self,start,distance):##generate easing steps data + current_time = 0.0 + start_posx = 0.0 + current_posx = start_posx + final_posx = float(distance) + posx_init = start + dur = self._EasingDur + last_posx = 0.0 + all_last_posx = [] + for i in range(0,distance*dur): + current_posx = easing.SineIn(current_time,start_posx,final_posx-start_posx,float(dur)) + if current_posx >= final_posx: + current_posx = final_posx + + dx = current_posx - last_posx + all_last_posx.append(int(dx)) + current_time+=1 + last_posx = current_posx + if current_posx >= final_posx: + break + c = 0 + for i in all_last_posx: + c+=i + if c < final_posx -start_posx: + all_last_posx.append(final_posx - c) + + return all_last_posx + + def IconSmoothUp(self,icon_ew): + data = self.EasingData(self._PosX,icon_ew) + data2 = self.IconStepMoveData(self._SelectedIconTopOffset,len(data)) + + for i,v in enumerate(data): + + self.ClearCanvas() + + self._Icons[self._IconIndex]._PosY -= data2[i] + + if self._Icons[self._PrevIconIndex]._PosY < self._Height/2: + self._Icons[self._PrevIconIndex]._PosY+=data2[i] + self.DrawIcons() + self._Screen.SwapAndShow() + + def IconsEasingLeft(self,icon_ew): + + data = self.EasingData(self._PosX,icon_ew) + data2 = self.IconStepMoveData(self._SelectedIconTopOffset,len(data)) + + for i,v in enumerate(data): + + self.ClearCanvas() + self._PosX -= v + + self._Icons[self._IconIndex]._PosY -= data2[i] + + if self._Icons[self._PrevIconIndex]._PosY < self._Height/2: + self._Icons[self._PrevIconIndex]._PosY+=data2[i] + self.DrawIcons() + self._Screen.SwapAndShow() + + def IconsEasingRight(self,icon_ew): + + data = self.EasingData(self._PosX,icon_ew) + data2 = self.IconStepMoveData(self._SelectedIconTopOffset,len(data)) + + for i,v in enumerate(data): + self.ClearCanvas() + self._PosX += v + + + self._Icons[self._IconIndex]._PosY-=data2[i] + + if self._Icons[self._PrevIconIndex]._PosY < self._Height/2: + self._Icons[self._PrevIconIndex]._PosY+=data2[i] + + self.DrawIcons() + self._Screen.SwapAndShow() + + def EasingLeft(self,ew): #ew int + + data = self.EasingData(self._PosX,ew) + + for i in data: + self._PosX -=i + self.Draw() + self._Screen.SwapAndShow() + + def EasingRight(self,ew): + + data = self.EasingData(self._PosX,ew) + + for i in data: + self._PosX += i + self.Draw() + self._Screen.SwapAndShow() + + def MoveLeft(self,ew): + self._PosX -= ew + def MoveRight(self,ew): + self._PosX += ew + + def ResetPageSelector(self): + self._PsIndex = 0 + self._IconIndex = 0 + self._Ps._OnShow = True + + def DrawPageSelector(self): + if self._Ps._OnShow == True: + self._Ps.Draw() + + def MoveIconIndexPrev(self): + + self._IconIndex-=1 + if self._IconIndex < 0: + self._IconIndex = 0 + self._PrevIconIndex = self._IconIndex + return False + self._PrevIconIndex = self._IconIndex+1 + return True + + def MoveIconIndexNext(self): + #True for Moved,False is boundary + self._IconIndex+=1 + if self._IconIndex > (self._IconNumbers - 1): + self._IconIndex = self._IconNumbers -1 + self._PrevIconIndex = self._IconIndex + return False + self._PrevIconIndex = self._IconIndex-1 + return True + + + def IconClick(self): + + if self._IconIndex > (len(self._Icons) -1): + return + + cur_icon = self._Icons[self._IconIndex] + if self._Ps._OnShow == False: + return + if cur_icon._MyType == ICON_TYPES["EXE"]: + print("IconClick: %s %d"%(cur_icon._CmdPath,cur_icon._Index)) + self._Screen.RunEXE(cur_icon._CmdPath) + + elif cur_icon._MyType == ICON_TYPES["DIR"]: + child_page = self._Icons[self._IconIndex]._LinkPage + if child_page != None: + child_page.Draw() + self._Screen._MyPageStack.Push(self._Screen._CurrentPage) + self._Screen._CurrentPage = child_page + elif cur_icon._MyType == ICON_TYPES["FUNC"]: + print("IconClick API: %d"%(cur_icon._Index)) + print("%s"% cur_icon._CmdPath) + api_cb = getattr(cur_icon._CmdPath,"API",None) + if api_cb != None: + if callable(api_cb): + cur_icon._CmdPath.API(self._Screen) + elif cur_icon._MyType == ICON_TYPES["Emulator"]: + cur_icon._CmdPath.API(self._Screen) + + def ReturnToUpLevelPage(self): + pop_page,ok = self._Screen._MyPageStack.Pop() + if ok == True: + #self._Screen._CurrentPage.ResetPageSelector() + pop_page.Draw() + self._Screen._CurrentPage = pop_page + on_return_back_cb = getattr(self._Screen._CurrentPage,"OnReturnBackCb",None) + if on_return_back_cb != None: + if callable(on_return_back_cb): + self._Screen._CurrentPage.OnReturnBackCb() + else: + if self._Screen._MyPageStack.Length() == 0: + if len(self._Screen._Pages) > 0: + self._Screen._CurrentPage = self._Screen._Pages[self._Screen._PageIndex] + self._Screen._CurrentPage.Draw() + print("OnTopLevel ",self._Screen._PageIndex) + + def ClearCanvas(self): + self._CanvasHWND.fill((255,255,255)) + + def ClearIcons(self): + for i in range(0,self._IconNumbers): + self._Icons[i].Clear() + + def DrawIcons(self): + for i in range(0,self._IconNumbers): + self._Icons[i].Draw() + + + def KeyDown(self,event):##default keydown,every inherited page class should have it's own KeyDown + if event.key == CurKeys["A"]: + if self._FootMsg[3] == "Back": + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + return + + if event.key == CurKeys["Menu"]: + self.ReturnToUpLevelPage() + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Right"]: + if self.MoveIconIndexNext() == True: + if self._IconIndex == (self._IconNumbers -1) or self._PrevIconIndex == 0: + self.IconSmoothUp(icon_width+ self._PageIconMargin) # only move up selected icon,no horizontal translation + else: + self.IconsEasingLeft(icon_width + self._PageIconMargin) + + self._PsIndex = self._IconIndex + self._Screen.Draw() + self._Screen.SwapAndShow() + + + if event.key == CurKeys["Left"]: + if self.MoveIconIndexPrev() == True: + if self._IconIndex == 0 or self._PrevIconIndex == (self._IconNumbers -1): + self.IconSmoothUp(icon_width+ self._PageIconMargin) + else: + self.IconsEasingRight(icon_width + self._PageIconMargin) + + self._PsIndex = self._IconIndex + self._Screen.Draw() + self._Screen.SwapAndShow() + + if event.key == CurKeys["Enter"]: + self.IconClick() + self._Screen.Draw() + self._Screen.SwapAndShow() + + def Draw(self): + self.ClearCanvas() + self.DrawIcons() + self.DrawPageSelector() + + diff --git a/sys.py/UI/scroller.py b/sys.py/UI/scroller.py new file mode 100644 index 0000000..d5d3b96 --- /dev/null +++ b/sys.py/UI/scroller.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +import pygame + +from util_funcs import midRect + +from libs.roundrects import aa_round_rect + +class ListScroller(object): + _PosX = 0 + _PosY = 0 + _Width = 7 + _Height = 0 + _MinHeight = 6 ## tested + _Parent = None + _Color = pygame.Color(131,199,219) + + _StartX = 0 + _StartY = 0 + _EndX = 0 + _EndY = 0 + _Value = 0 + _CanvasHWND = None + + def __init__(self): + pass + + def Init(self): + self.SetCanvasHWND(self._Parent._CanvasHWND) + + def SetCanvasHWND(self,canvas): + self._CanvasHWND = canvas + + def AnimateDraw(self,x2,y2): + pass + + def UpdateSize(self,bigheight,dirtyheight): + bodyheight = float(self._Parent._Height) / float(bigheight) + if bodyheight > 1: + bodyheight = 1 ## 100% + + margin = 4 + self._Height = bodyheight * self._Parent._Height - margin ## Draw body + + if self._Height < self._MinHeight: + self._Height = self._MinHeight + + self._StartX = self._Width/2 + self._StartY = margin/2+self._Height/2 + + self._EndX = self._Width/2 + self._EndY = self._Parent._Height - margin/2 - self._Height/2 + + process = float(dirtyheight) / float(bigheight) + value = process* (self._EndY - self._StartY) + + self._Value = int(value) + + def Draw(self): + + start_rect = midRect(self._PosX+self._StartX,self._StartY+self._Value,self._Width,self._Height,self._Parent._Width,self._Parent._Height) + aa_round_rect(self._CanvasHWND,start_rect, self._Color,3,0, self._Color) diff --git a/sys.py/UI/simple_name_space.py b/sys.py/UI/simple_name_space.py new file mode 100644 index 0000000..e9e7073 --- /dev/null +++ b/sys.py/UI/simple_name_space.py @@ -0,0 +1,12 @@ + +class SimpleNamespace: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ diff --git a/sys.py/UI/slider.py b/sys.py/UI/slider.py new file mode 100644 index 0000000..864ce0e --- /dev/null +++ b/sys.py/UI/slider.py @@ -0,0 +1,40 @@ + + +class Slider(object): + _PosX = 0 + _PosY = 0 + _Width = 0 + _Height = 0 + + _Value = 0 + _CanvasHWND = None + + _Range = [] + + def __init__(self): + self._Range = [0,255] + + def Init(self): + self._Value = 0 + + def SetValue(self,v): + self._Value = int(v) + + def SetRange(self,m1,m2): + if m1 >= m2: + return + + self._Range[0] = m1 + self._Range[1] = m2 + + + def SetCanvasHWND(self,cav): + self._CanvasHWND = cav + + def KeyDown(self): + pass + + def Draw(self): + pass + + diff --git a/sys.py/UI/title_bar.py b/sys.py/UI/title_bar.py new file mode 100644 index 0000000..b9e1d22 --- /dev/null +++ b/sys.py/UI/title_bar.py @@ -0,0 +1,290 @@ +# -*- coding: utf-8 -*- + +import pygame +import os +import sys + +from datetime import datetime + +#from beeprint import pp + +import alsaaudio + +##local import +from constants import ICON_TYPES,Width,Height +from fonts import fonts +from icon_item import IconItem +from multi_icon_item import MultiIconItem + +from util_funcs import midRect,SwapAndShow + +from config import Battery + +from libs.roundrects import aa_round_rect + +from libs.DBUS import is_wifi_connected_now,wifi_strength + +icon_base_path = "gameshell/titlebar_icons/" +class TitleBar: + _PosX = 0 + _PosY = 0 + _Width = Width + _Height = 25 + _IconColor = pygame.Color(114,114,144) + _BarHeight = 24.5 + _LOffset = 3 + _ROffset = 3 + _BgColor = pygame.Color(228,228,228) + _TxtColor = pygame.Color(83,83,83) + _BottomLineColor = pygame.Color(169,169,169) + _Icons = {} + _icon_width = 18 + _icon_height = 18 + _BorderWidth = 1 + _CanvasHWND = None + _HWND = None + _Title = "" + + _InLowBackLight = -1 + + def __init__(self): + self._Icons = {} + + + def GObjectRoundRobin(self): + if self._InLowBackLight < 0: + self.CheckBatteryStat() + self.SyncSoundVolume() + self.UpdateWifiStrength() + SwapAndShow() + else: + self._InLowBackLight+=1 + + if self._InLowBackLight > 10: + self.CheckBatteryStat() + self.SyncSoundVolume() + self.UpdateWifiStrength() + SwapAndShow() + self._InLowBackLight = 0 + + return True + + def UpdateWifiStrength(self): + self.Draw(self._Title) + + def GetWifiStrength(self,stren): ##invoke anytime to change title bar icon status + #0-25 + #25-50 + #50-75 + #75-100 + + segs = [[-2,-1], [0,25],[25,50],[50,75],[75,100] ] + + stren = int(stren) + ge = 0 + + if stren == 0: + return ge + + for i,v in enumerate(segs): + if stren >= v[0] and stren <= v[1]: + ge = i + break + + return ge + + def SyncSoundVolume(self): + m = alsaaudio.Mixer() + vol = m.getvolume()[0] + + snd_segs = [ [0,10],[10,30],[30,70],[70,100] ] + + ge = 0 + for i,v in enumerate(snd_segs): + if vol >= v[0] and vol <= v[1]: + ge = i + break + + self._Icons["soundvolume"]._IconIndex = ge + self._Icons["sound"] = self._Icons["soundvolume"] + + + def SetSoundVolume(self,vol): + pass + + def CheckBatteryStat(self): ## 100ms check interval + content = "" + bat_uevent = {} + bat_segs=[ [0,6],[7,15],[16,20], [21,30],[31,50],[51,60], [61,80],[81,90],[91,100] ] + + try: + f = open(Battery) + except IOError: + self._Icons["battery"] = self._Icons["battery_unknown"] + print("CheckBatteryStat open failed") + return False + else: + with f: + content = f.readlines() + content = [x.strip() for x in content] + f.close() + + for i in content: + pis = i.split("=") + if len(pis) > 1: + bat_uevent[pis[0]] = pis[1] + + # print(bat_uevent["POWER_SUPPLY_CAPACITY"]) + + """ + power_current = int(bat_uevent["POWER_SUPPLY_CURRENT_NOW"]) + if power_current < 270000: + self._Icons["battery"] = self._Icons["battery_unknown"] + return False + """ + + if "POWER_SUPPLY_CAPACITY" in bat_uevent: + cur_cap = int(bat_uevent["POWER_SUPPLY_CAPACITY"]) + else: + cur_cap = 0 + + cap_ge = 0 + + for i,v in enumerate(bat_segs): + if cur_cap >= v[0] and cur_cap <= v[1]: + cap_ge = i + break + + if bat_uevent["POWER_SUPPLY_STATUS"] == "Charging": + self._Icons["battery_charging"]._IconIndex = cap_ge + self._Icons["battery"] = self._Icons["battery_charging"] + + print("Charging %d" % cap_ge) + else: + self._Icons["battery_discharging"]._IconIndex = cap_ge + self._Icons["battery"] = self._Icons["battery_discharging"] + print("Discharging %d" % cap_ge) + + + return True + + def SetBatteryStat(self,bat): + pass + + def Init(self,screen): + + + start_x = 0 + self._CanvasHWND = pygame.Surface((self._Width,self._Height)) + self._HWND = screen + + icon_snd = IconItem() + icon_snd._MyType = ICON_TYPES["STAT"] + icon_snd._Parent = self + icon_snd._ImageName = icon_base_path+"ic_volume_up_black.png" + icon_snd.Adjust(start_x,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + icon_snd.ChangeImgSurfColor(self._IconColor) + + + icon_wifi_status = MultiIconItem() + icon_wifi_status._MyType = ICON_TYPES["STAT"] + icon_wifi_status._ImageName = icon_base_path+"wifi.png" + icon_wifi_status._Parent = self + icon_wifi_status.Adjust(start_x+self._icon_width+5,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + self._Icons["wifistatus"] = icon_wifi_status + + battery_charging = MultiIconItem() + battery_charging._MyType = ICON_TYPES["STAT"] + battery_charging._Parent = self + battery_charging._ImageName = icon_base_path+"withcharging.png" + battery_charging.Adjust(start_x+self._icon_width+self._icon_width+8,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + + self._Icons["battery_charging"] = battery_charging + + battery_discharging = MultiIconItem() + battery_discharging._MyType = ICON_TYPES["STAT"] + battery_discharging._Parent = self + battery_discharging._ImageName = icon_base_path+"without_charging.png" + battery_discharging.Adjust(start_x+self._icon_width+self._icon_width+8,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + + self._Icons["battery_discharging"] = battery_discharging + + battery_unknown = IconItem() + battery_unknown._MyType = ICON_TYPES["STAT"] + battery_unknown._Parent = self + battery_unknown._ImageName = icon_base_path+"battery_unknown.png" + battery_unknown.Adjust(start_x+self._icon_width+self._icon_width+8,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + + self._Icons["battery_unknown"] = battery_unknown + + self.CheckBatteryStat() + + sound_volume = MultiIconItem() + sound_volume._MyType = ICON_TYPES["STAT"] + sound_volume._Parent = self + sound_volume._ImageName = icon_base_path+"soundvolume.png" + sound_volume.Adjust(start_x+self._icon_width+self._icon_width+8,self._icon_height/2+(self._BarHeight-self._icon_height)/2,self._icon_width,self._icon_height,0) + + self._Icons["soundvolume"] = sound_volume + + self.SyncSoundVolume() + + if is_wifi_connected_now(): + print("wifi is connected") + print( wifi_strength()) + + def ClearCanvas(self): + self._CanvasHWND.fill((0,0,0)) + aa_round_rect(self._CanvasHWND, + (0,0,self._Width,self._Height),self._BgColor,8,0, self._BgColor) + + pygame.draw.rect(self._CanvasHWND,self._BgColor,(0,self._Height/2,Width,self._BarHeight), 0 ) + + def Draw(self,title): + self.ClearCanvas() + + self._Title = title + + cur_time = datetime.now().strftime("%H:%M") + time_text_size = fonts["varela12"].size(cur_time) + title_text_size = fonts["varela16"].size(title) + + self._CanvasHWND.blit(fonts["varela16"].render(title,True,self._TxtColor),midRect(title_text_size[0]/2+self._LOffset, + title_text_size[1]/2+(self._BarHeight-title_text_size[1])/2, + title_text_size[0],title_text_size[1],Width,Height)) + self._CanvasHWND.blit(fonts["varela12"].render(cur_time,True,self._TxtColor),midRect(Width-time_text_size[0]/2-self._ROffset, + time_text_size[1]/2+(self._BarHeight-time_text_size[1])/2, + time_text_size[0],time_text_size[1],Width,Height)) + + start_x = Width-time_text_size[0]-self._ROffset-self._icon_width*3 # near by the time_text + + self._Icons["sound"].NewCoord(start_x, self._icon_height/2+(self._BarHeight-self._icon_height)/2) + + #self._Icons["wifi"].NewCoord(start_x+self._icon_width+5, self._icon_height/2+(self._BarHeight-self._icon_height)/2) + + self._Icons["battery"].NewCoord(start_x+self._icon_width+self._icon_width+8,self._icon_height/2+(self._BarHeight-self._icon_height)/2) + + + if is_wifi_connected_now(): + ge = self.GetWifiStrength(wifi_strength()) + if ge > 0: + self._Icons["wifistatus"]._IconIndex = ge + self._Icons["wifistatus"].NewCoord(start_x+self._icon_width+5,self._icon_height/2+(self._BarHeight-self._icon_height)/2) + self._Icons["wifistatus"].Draw() + else: + self._Icons["wifistatus"]._IconIndex = 0 + self._Icons["wifistatus"].Draw() + print("strength error") + else: + self._Icons["wifistatus"]._IconIndex = 0 + self._Icons["wifistatus"].NewCoord(start_x+self._icon_width+5,self._icon_height/2+(self._BarHeight-self._icon_height)/2) + self._Icons["wifistatus"].Draw() + + self._Icons["sound"].Draw() + + self._Icons["battery"].Draw() + + pygame.draw.line(self._CanvasHWND,self._BottomLineColor,(0,self._BarHeight),(self._Width,self._BarHeight),self._BorderWidth) + + if self._HWND != None: + self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,self._Width,self._Height)) diff --git a/sys.py/UI/untitled_icon.py b/sys.py/UI/untitled_icon.py new file mode 100644 index 0000000..b13b675 --- /dev/null +++ b/sys.py/UI/untitled_icon.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import pygame +from pygame.locals import * +from sys import exit +import os +import sys + +from datetime import datetime + +import base64 +from beeprint import pp + +from util_funcs import midRect +from fonts import fonts + +BlankPng = "gameshell/blank.png" ## 80x80 +## use blank circle as bg, Two alpha As Icon Label +#Upper and Lower +class UntitledIcon(object): + _PosX = 0 + _PosY = 0 + _Width = 80 + _Height = 80 + + _Words = ["G","s"] + _FontObj= fonts["varela40"] + + _BG = None ## initial surface + + _Color = pygame.Color(83,83,83) + + + def __init__(self): + self._Words = ["G","s"] + + def Init(self): + self._BG = pygame.image.load(BlankPng).convert_alpha() + + + def SetWords(self,TwoWords): + if len(TwoWords) == 1: + self._Words[0] = TwoWords[0].upper() + + if len(TwoWords) == 2: + self._Words[0] = TwoWords[0].upper() + self._Words[1] = TwoWords[1].lower() + + self._Text = self._FontObj.render("".join(self._Words),True,self._Color) + + def Draw(self): + if self._BG != None: + w_ = self._Text.get_width() + h_ = self._Text.get_height() + + self._BG.blit(self._Text,midRect(self._Width/2,self._Height/2,w_,h_,self._Width,self._Height)) + + def Surface(self): + self.Draw() + + return self._BG + + diff --git a/sys.py/UI/util_funcs.py b/sys.py/UI/util_funcs.py new file mode 100644 index 0000000..d08764e --- /dev/null +++ b/sys.py/UI/util_funcs.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- + +import pygame +import os + +#from libs import easing +#from datetime import datetime + +#import base64 +#from beeprint import pp +import string +from Xlib import X,display + +def X_center_mouse(): + d = display.Display() + s = d.screen() + root = s.root + width = s.width_in_pixels + height = s.height_in_pixels +# print(width,height) + root.warp_pointer(width/2,height/2) + + d.sync() + + +def IsPythonPackage(self,dirname): + files = os.listdir(dirname) + for i in sorted(files): + if i.endswith("__init__.py"): + return True + return False + +def MakeExecutable(path): + mode = os.stat(path).st_mode + mode |= (mode & 0o444) >> 2 # copy R bits to X + os.chmod(path, mode) + +def GetExePath():# get self dir + #dir_path = os.path.dirname(os.path.realpath(__file__)) + dir_path = os.getcwd() + return dir_path + +def CmdClean(cmdpath):#escape spec chars + spchars = "\\`$();|{}&'\"*?<>[]!^~-#\n\r " + for i in spchars: + cmdpath = string.replace(cmdpath,i,"\\"+i) + + return cmdpath + +def ReplaceSuffix(orig_file_str,new_ext): + filename,ext = os.path.splitext(orig_file_str) + ext = ext.strip() + if ext != "": + return "%s.%s"%(filename,new_ext) + +def FileExists(name): # both file and dir checked + return os.path.exists(name) + + +def ReadTheFileContent(filename): + data = "" + with open(filename, 'r') as myfile: + data = myfile.read() + return data + +def midRect(x,y,width,height,canWidth,canHeight): + return pygame.Rect(min(canWidth,x-width/2),min(canHeight,y-height/2),width,height) + +#surface color change +def color_surface(surface, color): + red = color.r + green = color.g + blue = color.b + arr = pygame.surfarray.pixels3d(surface) + arr[:,:,0] = red + arr[:,:,1] = green + arr[:,:,2] = blue + + +def DrawText(canvas,text, x,y,width,height,canWidth,canHeight,fontObj):# text for content,fontObj for pygame.font.Font + _w = 0 + _tp = len(text) + + for idx,t in enumerate(fontObj.metrics(text)): + _w = _w + t[1] - t[0] + if _w > icon_width: + _tp = idx + break + width = _w #recalc the width of text + if width > icon_width: ##Label width max is icon width + width = icon_width + + if _tp < len(text):##cut the text to fit width + text = text[0:_tp] + + canvas.blit(fontObj.render(text,True,(83,83,83)),midRect(x,y,width,height,canWidth,canHeight)) + + +def SwapAndShow(): + pygame.display.update() diff --git a/sys.py/config.py b/sys.py/config.py new file mode 100644 index 0000000..b9c8025 --- /dev/null +++ b/sys.py/config.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +CurKeySet = "GameShell" ## >>> PC or GameShell <<< + +DontLeave = False + +BackLight = "/proc/driver/backlight" +Battery = "/sys/class/power_supply/axp20x-battery/uevent" + + +MPD_socket = "/tmp/mpd.socket" + +UPDATE_URL="http://192.168.31.236:81/version.php" + + +VERSION="1.0" diff --git a/sys.py/gameshell/blank.png b/sys.py/gameshell/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..077c8a60659f5389b1062f5338b30ab0b8f259db GIT binary patch literal 2101 zcmV-52+H?~P)z&mL|1d(J$y%2M|4$uh!565DBRdl(5yof)D?sh-cVfPU08iQFlO}rM~i9?d>$KeKCf^TA( zHh1q)Z5~9(T9~upI&}6w`_M$+^9_RlKJbU~L=fX_d9r zy5CT>Wp8qAzOZ@Wa4Q*(fQRBntPn5wg1?D((xPhlmFDllmea&wRWMQ5eiIHG>dl8L z;3V9OCCRy+_?mdBKQL5-I>-Zs3!dy{4boGjiGQ0C<-Pc

KGWncZR#DF)2xLYs_%0*)6Ojs>^i;SLyA z7XBo5Yzi(k;G51s0Y9xa1K*9yJ7rmkxLmCVP8J&NqcWg;L2|qT8#`%PN$3|nvETxU zZmEFt7)5avE|bvcvQR(3(hMFg@9wlNAH50yI@q=_)$_m zOZ9tnA>bKmqsxopYgUh(+tqg4#|h0c0Sg`!PCPwwo)r!O1@EOM;JoC*o!zpig#0!s z&m#n!EccRH-9>}S#_FWphshPW3&eIKYtx`r)(K~*f;ko8+*q)|$Xxd}%WkYsj%QWm z-FJf?jYu_`QNgjviO0HZOc_ZvI<6vok_#U1vN2`kiKKjFMeY-_t;@!gk>`{0q>9`{ z_IW)Tkv?SODr%tw126WdPx_FJZI_4Yh@~R;UhhN*!okK7NqI*_KC?bTk4B^q+4hQj z=cV@qHYR<@{#(IQ$%(0YG~%eF{A5K$^AsGbMTt=O^Zs#sY$sXPZukZdy@+v=$bj@ ztSs~zONG0T(k$s#bXM=)E&0v7CZeMtpeC3k0A+F_&0M5@I`$)KAGxJTp{ zC^%Q{?lw}HA+#vCK@tT&a6@&uS`wQvdLF8T2F2=Zi~LphG6A>YTgmac^1eUPI7$BW zU1Pv!k9u4B-^0JFffL(M zPph0L*01ZUwxpB)J4JlVpp)cgXqSiTW|10Xp?s9Ch%}s!h};WH)z9qPE~bdI%MVxU z{II-@o2xd?H^}m=722J}mq>%WM5IBkmjSJtX^;z^5vg2ma)75nPAL*`FEzN^T)Pu- zr#vRY^*fe`yORPIR77ZKkqUm?;J-d>wu|q!%f*+*(x!qh*dr1e-XpsA>0-8jns|{9 zcV}m>NNIe(@K;&oAp6ekF!2%@Z&z!ppq*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W> zdx@v7EBhl>J|QiY&uh2$0);rs3LMjcG{{yV24#ypF9rr?15X#n5Rc<;ub$7I<0^Cf zV|ZOc1|K8;+9Q`1G|tFc%lX1dpOJHE&Z4BN-4$CJ!&bKc<=%GQ{z6b-h64AzCDU&O zCm#@K{gT4M-RY#`wrSBy(J5>>C%vTjKcBHX|IZ`Gzv_j!*u0sQ@6SB9{yg)2$s3M| z1`8L-=U6=bTx4}=p|X?8{#VO&8hbxYeig7>=a(k;{*q%RpDwJsI9KEOEKlwU3vRmv zWzMmYy>l|?#TBuWFV|d#dduxT? z&uA%p{#Z<=P->d2fnE=91m_n?cP5ji+;!zwXCyKoJF}8)#p#fSuEL3--uFICQ#Cir zJ<7nFX1n=?;cWke4Nvd+-csu5o0zs;?Dk@=bvsrim^5EwHW82dEvmaJYU_#&1*gx9 zt8TO{UL|&*Y1MP)O}T1sespEN_ihxJf2==KJFwzp&F#nW0bSO9XSMCh_A~CtzqL9` z(&cQV{*H{R_y2vG+wC@|@O}K+ZxcS=x7_Tz#p?6kUmL$^N%GA^ksKWEM5Jf5?2=6b)i zrjrHjcf4~5n;SLPa?N@3Rc3pqIG#OMb8%X2+uvsyYWu{tC7gYzP{omd{pwQJTw8}? zMys^b?JspJFMG5|&HXuL{cG{eK>r=hx6@ucdU|_az=LS}t5@%q+WvVs$$wu}h_U>I znT%d@BQLR=)kB_ literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/about_bg.png b/sys.py/gameshell/icons/about_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..991fa5c34fe936ad675a61f5d15f8a5fad1ac916 GIT binary patch literal 12219 zcmaKS1yoSo*6pD~N6Q*bx*G%pM7q1X8{X!-|9$tq zG5+y}13ezjIeV|YSIjxr4wjPg&+uBQbJT7f?x!}4}^>e?zI2<(GC7V)cqhX z3Ozjj$!N-p0e4WWB~F|InT#Qa6(Mqg`+lRgxdEo+N!QCcBt)*w?oN0G z%hF)o@MllNgm%%fiBWxYGADJu%o%+4^lA-j=y$}MG?*Vcxuj(^8cazv&>l)u5QBt+ z26PAl3U*i~S-%KUFuiOlD}5q`OzGecOF`*ZLknRGJBR+~@`J_%0)^jbnremNn8Q;D zBIYp9-+2yY5P^q#@)0E@`0})ND&msjOJajfz5TG}vmgoNC5DRJa4ccdZ|r&PiMGAcJe|rgY>|LNQ1LM#4Fi}`(g#+ID zp>3EMpPBp)eM%TStX{)J|Ci;ausH1^*M`CQoYDiTSy~Hb992Vlucfu|#vl~RPx(lU zdJ5Z>7c(Qcy#IHk0^z0t)8eVZriJmOx`02dg2K+fVkrz{C@QKxbL3_{F7^NHOH!*J zR#S({HusMtF?K;=WUG%5mER#A`xg{SC4-#-EeBi(WrdAQPAmDRxTzttyvuRce?3dQ!1TnCP z#JU5}ub*9Yb?l#L?BFOK=im4ayk#ySJqnFY^~uF16$Z zUk{bXbU2vTT#>a%{os*Y=b~b(;V`VBBfOosKi9BwdvzjKGuByWy3Nb(5BBI#&;o(l z4b>9@d$B}%W=KO^R#{UnU^cN|8OeI4F*Xp0)eaZ-%zkHFdSzjkT*Ra*lDTpr)^789 zN{;{fJh^}NMjA|Kn^}{c_X7{nDQVsTVMg<`riGI|fiN(OY#%bSB1o0V>KeIuil;cRi*SFd0oi5z)n7ne-Q7#ib| zTy$>xO{hRoEqm>slot~nooVMLBO{X<_ewy3JP?z-yFejNPDLf3Rr>R1N?SX-$FGs_ zx#BW1GH%W_a3W%op$Q1I5WT_&7VJt%N(Q$j4d2~dI9PJxK?W>@8k(9VTJueA7rGmR zskCq2aPHJ*Wj*ig=~>wv&L-eECBL_CrV1|lIPW&Yd_e|oS$4n-v=hx+-!0}Eo^N^?t$hSHERF~xVr zOJJbH#Kg<1jr8cm#NM1lJ~xhf`z`sBonJ#kJ%yi&4lj@Niss2VIXT@=hsBL2%Q6S= z)=LpZ9y3{ow((a#^+EuL7RU2h$C6M8qc{$#=>v6=YzB4KXN3g~sC zo$gEwWtq`3GIskQJdsmWRD5wEK^M1tG9)lDrI80o1d)#5Ll?VKzN9>kqiS44oR(q0 zJT?2>t&4-jMudOmCUROto6W%Lb=|Zs1LYaY&C_=ocT4 zZ#bBOl3BLiT;1e)*6s}hN6+neu|HSkDO;%i9_6SUk3?zn;>@^%3=ZJ>1_JFElqz^-#Zjnf>^R-gK2& z;NhV)JR+hn1r`M>D^5=YsmS~HA-yh4XuhHNZ=TcB|IXUUc^q)6#j8bf3lW{juT-<4 zePMwd0Rf@bagS(ud6^=n7mdv6Z2Qyd+FIMh#6+>JW}R(U@#zckh|UCNJ+^8WH@EB$ z@r;vAZZ)dXGq06PRY)L^B!Wntk5^GJNV)a@wk}_tZ0H@Y_HKgRa(8#f!o~G_`a*1R zL{@;kz-}c{;9k(uvQ%}g!EPg>*F?R_^b_@E0WrHN?sTPzo~@_T!F<)~H`qtu!%^`4 zJ3Mf@F5vOj7yFtZ?y`%Ee{b3-dWHrz0t1!Z-S?>qt2Ih@F zki_oW(jwq`u^;a34K?*7u&(HxZVro$gb}iKui6BSC6b9hdRuIAve|hQwydly8p+!~ zIz*V5n82qnQBY9CPWt=%M-p6E9CuZkOr-aMu!2R6hqL^L?Z15aVr^~BwB=&tz%2v{ zdJsBxu&5z94hf05JhunCs7Oj`D*8SrlmAqf84jvH-UZq}HH72W*EcPq&zWDPQn!t% zxTK_{NcH6v7o!1ZU`^Tw*+`BSmu78ibJO_ztPB)iUEklN^03g>))q}pg??dD^E)Zk zw6rvujVh}J{%n);{W&vR&oR}GD3Gu?@`Zd6H-R6)!oo_GF7Y}aLY$VfOiBAcTJBk1 z349TRw6(SEw?|P@d7L;BszKVJ(Vp>tf^U7iD%o^1l*Si2k|&#-lHQzCE>$lU8cF4+ zl3F^Z6A}^<6(1j1Tl+SJ%dUIzXn%iSrCb+bc6Qc&|6x7Z%KB*eov+K~^|kKyXo2x) z{?o?BMk%_BV4Rm_HY;KkrBxt!2?RWOYwD|nGh5dOk_Xa0vY7m(@(T=v%NWx^E^yM& z&;S7!1VVsVT3t@g`{AbLq3Gw&6Amh-huU|UMA&j zCi4$fT3j`@)KcIdhU6WoBg-#9?dof5Yx^FT^aK_4a7f_6*!(p;edpQEL^#>b=4KE; zD#@B01&SeQd~Qh&zl(k}7Y;YZOHhFsHmbV#1q5uD>?Qd;R{PFad^i+%*f3&F89jBd z)ir3%lrO}>nCduHlQXAC>(PkV^82bi@2(Ta;7UDG*8vs}A|1Xc#dH9k8W0-#A#M*C zz~Ne4KCdNmdwY90Yd4e_TMgWn+s!IRzMz-=Pgxlgo5|nK|8jnm%rGqBc(k$mo#Odv)LZ3th=rkVe(5b-d8_r}+DAKpB&w6sj%ut@4!(+AkJ zQ^tI|}l5Y@d_k69zP1qHc6dp>F|_NhlrzOG0-?2i)Ft)kuTD+qaX( zSelK_<)6mi{gZ^=+HXcl9_*5G+vm64y=gr38RiWM4J~)vQw#0i-qHAGVf#?qITAm7t=g7AjEG z?F@Pm?LhVNrQYhVXbEZQE|5O*hwE4@XWycc^Uu;vyom2Vnz3rJVHNQ4^*ve& zBu}z8x3_2GOvzLJp_TB~CTrNlY8}Im#%~g>F`&8tNY^L zzJC1#Y|`Q3VcB*aD+`P1*<1&E+bj_UNc`qO)Kvt>Xafi|epopF;tu~iCTYS?z~Y&x zG*Q@JP*qj^R9#@j8O#6rb?8i9n$|>})PO8sctPhqUBP0L0s-T`3yD-Tc znO90ZNvN39Sf96gBy+6ORU+TY)$r9te)skLR7026MlY*=qzy)eIE|ye84Pj#@}nri z$dQQ(JMv!lM!h!cyG13_6kv{>0V>BN{Yj}5+a~s7zohoyhh@~&Qz;%Xv*T~IP|8t7 z*0J$nU1UEj-REE^T@hoh;_b{w%90QS!sTD2>Fqd~r#!xI@_^94@ic(e3QNf4a8-57 zq1Q*#CTMXY+I6Et7P|}Us{=hY5q_g))n&_}XYhohewQamcoGcZn*SJRZo?{OamW~jdGjm zep$kuU8dOiq_r}(@T%kL?tgfSc@P)o@m2kDC?y+M@&92v$R&she2Q>XeptfDl>hw~ zfA(jlj7%iI^xvc`^5nxz1pk)-G6cwBGNbv%NJ>gb%7__?A*S(rpqj3!i&0>;&UL_n z(y)n36vF@cGZt#WyqxyoJs~95k_M9|A=O!3q8^gZaE_;*VFWcRl0# zs$o_^RBvco7P@^!gD8$c&ap!v`{s@P{$GTRp)`Lfnxbf1$sGAplqVf=UiU%~;Yfzn z0rB?Ber@(kLjsYUhJ(dB%SulW1WjQlhy^TWjU5l+yth~lGu$G{kTEd2BZ)aiC8o4O zgIc39MWS6-5Wg{~%1_k1|Isa-v^aM$4+rTR8BnsaDp*ZtA00tD7eY}7o1ew_toL_S zNx1BPW$E$p@CB3#zx{zP>#6Dz^_lghucdD<)T}K>j2|z!v`DZVf-y)T38*ettfI&j>?U zt!8}ElZTIVr}>`f;$UH7lHa5c7mb)-LoXTd3#*pqJXw13IqU1>v7y`7)ZNVGxTI* zunnxhLGuL}w|{_X?2El|hGOlSh}U*(_TEVI3w8*aoZoL?DTr(BQQLnLtAR{BA)6FRy=ueT86(CfQ-~}09 zZDk}mA1qXSz01mY$&A(!S7%V9q*CR|&igvKvf0yfw*1H4j<$8-Z&n5dblszh>(-T* zvXCP03-p~Ce8_rln!SP_PWR@~ANMz%<=(`-uM50lNkrS3DCrm&RdjZ9)7v%sAw-Y& z;$~knTyIis+D|g37OFAp!64yw#xgK4Ab4wx)h(Lf+6ee{;s1{Otv!aGkv=K4v{bp8GSXPQTDm!icHF`94iVhk*$FLc z_J{^SE0zK0O~&W;eCg7rwEArE-s5{t&gb8?HX1rJ6>gl%=LdoBbA%thz%kqWrFzTF zyGg@m%4ju8b6BbaGyO}>fba@y($yYK|~`# zh9smU1b4qN%-%OOICad-5Q_w&i-29Az(W4hAWIIl0(@9bPZ#nb+5PHg_la<|;TR}d z?mum*0T=|N;cz7Y{jJkEeLUmRBy$)cCq`<6Jq%Q7wxE?AW-(JG0%CS^vOwWo;`u%T z6z0A~4Yh75;+BHt1IQ?KhU}086*lzjfzZ9o zni3xi2M7Cbu4ZBT4lSh8hpwD_6u#Q7-joUk1_ts52+6u_h`>b>MfCYo5)u*!>bbuz zdY_~x9%=hErO1M}ahSnEVB4-N#kG7>Ov4x)h?Lzl!evsHB7l8UM-nGPQPocG9sN z$^UoqTTF~^SfAE-JP1#(Y%!3QF4ywX<WYDz9BkSkD(QbaWOSd$|k%u=MK zkWcQWaj<16D7IT45Yf@en7gU$Zj!S+*iH&As&Q+TQaXhq2YC|w1a2j z-0W;C6WWA2H>f+c8}@?lO2SG!di7a;p+1Rr*ua1?^zvjj$D10&JWG6hm$a_vqb(v> z(R>eDZZOpK^y*e}WztJ+X0_ws!c+mZe`|lJ6L+ydqcn~e8X6L%d4;wg$E>%wy z+5TM;qT@DNwdGQ#fv|$l1Yq7v%5+Z;3H*?FiA`xb#nQAwK+I`ro+BGG3hKIJsnHLu zPt`dfpp*=H+W!CqcyM>YtiLHImBLF{G*Q}BI962G+KVA&Q&^1NWjT)q0l|QB-17)Q zEP}{?>4lh%v^`tWC$fqX7H?7k$1t9aTTK4eQIioS@QvT80_6S5#jP7=s2Fk5kikAL+oW1wb*!2b8HqXWeuSXq zdSw98Hi=nQ_V9CBiR|CoFhBcvnW{c>tJj#fbv?naHS&`$!2Q_&&1MLNo!_@F%~aQm z7MiZ}yt}a*+8Zs=Y)Vqc$^_*&fyII-Ad=uCb*kH%9WMt{3YP~_;A^9}$82=#hm!Et zg@uUI1QwFuwt)@AAcKc+ad8(bJ{8qAB4Uz;yc)^15mA2si4Gb{+S+FCd)6OsNBYKU zFbM(hSLj()Kk>v5yDe-SY|s$tiL{%s%*{2mH%#Gi!@IfIgOg*fSUWpfVFopbt-u}6 zb^X+2w`iB?Bl&igCY<6z&$&hs`Ao4YYZxh~%nCuUsLy>Isd(r8pF;--DqA^2>!xf09L^IKy`R}>X#jZ^T-sxri2}gm#i4F)z;TP&o}r$5*E4=P@>Un zkHW>p1z@tbnZd|gAWY2ESgx4{6{n<-r0}`nm6nzsS~~?w(06y-x~Bk~G@%7g-vXt9ocCAwoZICgWxc~e1b}0+S@6`<)Q481 z$o%~LL03yiK)^7GeL>Ad2>7*cVBX7#l$MypU`-xJV|z#1XQ-#dOc1CB4UW6sKaX8r zxec{npZf!nI+)5+m-rftm;rLcBZgq(PcyTHYb~x>ST{{PUw_muYD@d&CY)P{;N=9A ze6RLrtm>w}G6FsZVwXtU`I{o$7}qn5{Dcr1EL6%7=2@|1(dSDF>v3+%qWcNkQ$ zv9}*3_-;Ypl=<^#ilznyo1CGzxOl!IT`7)ussxpfAcc3~&vVm!DUX{ok=2F+qNX?1 zAxgfP5ORhmykM&{7S*`k0qBfC$Z`<8oEI#G9P^M9C=1T=Tydc)@qvLz-C@Lm_*(Ae zrJ?c`xX>#IHeVYCE8qEBoc%KV*EiZ?j*cAq{{BEgM7nEE4*X1j>@QqfU-zhnWoBk> z&a||suDrG&MED1mY6Fgkiwl_$pn*=*|JxM!PX|kF5r}<(VB&%lf8~ki^nSy82L}g#{u&dL zxVoxOLq`_^(+X%`a`H2Aniysq8yh@wt5?vcL1KLT9u;eKDZ}tvhqwkBRoCYFYYpi} zn)z(%BL-Y7EN_{(4;4;8=@>}n?5VX`u@>2ji+B_&p9oapAAun8X^1;m-5f6=_VMvq zTweB}6bUf?t`{GM`j>U8IX@rS#KgpS>Zd_m)%GO*(=D}*L6zojxnpaKZN8HgMwrlI zPP|s3ZTR+uBu&f~Z)AasO8Xy|sU6O~~W^+Wv#B@M9fB*gky_GP*sQh4B{rvoxBqb%$ zI+&yYk%jZV!673XWJ2ozt6}%>tyf=4R1`j*=J|6U6imzw(}bm^C5@7pyN>lYF}(@D zeo0um>FZOyi0QT8*Bi?Gftas_$dL|iho7<}hJiPXY;A1|XJ77_A+)Zpt{RU*RsFcw z*jXQA#^VEB>!#cW)}Ij*_mh&7k(Jt52@4A+fH3vlFc5g+=p`@zb^fH>1lYMZ^hpzZ zCvGPsIC$y2yoG1_YU_GePfw41(8@HtqO`@SQ%vOrRA^zO+JcIRGLBwSQfL>{p4CHQ}=UP3T zosX{(U4%E?0|~3NIAE~f2Ik7#o4B+4V8c;E9~ z%ku1`Vgk_Egx^Fka!rW8JdY|LfFxnVo&=tz;pSE|l0!y8DflW-37c{4d8X%(8hm1 z{!n&vbwx=}j|Q|FSdg>=7rY)wLDP6~-YQdOBFQxuywCFzeYHQ#N#HubI_G?Kx2 z`|4G25ko~J_Goe|h+gjG)?Gdz9~V4bM-;4n)p;Kr7FNsx4_booaYGiMm4RippbSBn zypLxAE-{kcpFk+z!6w}LS%QiD1=7Jjf>WyqS!qV23aM{8?ijqY|uYoidz@T2ONT{cxJn5Yh8|znAR#wj_lPZtt zeI5WyA?;rw3A!1NLOuvYEFz+iWKOH))-P}ln)EViYDCYUKX)d-g)%;7i;ZS}?s0w) zfOOnI+s+hj4EpCx05ec&{(Ugvzb=!Wm?Hspc-lOvL4hSmg{``$K^GU|Mp;|x-rv_p z*WJjBCaPOL$PyPDn*o|usC6V57#Kh^sLBtktE-EO|3!(*cjJ}x%4l)o&yQ|x8ITN* zkB=8AqFYD$`*o(<+l7Cj8W@k(ehmK>tU?Y5&*9p%aSFc&5#Zsb>V(Mt&zYdcrlvo2 zAMhKGw~GLEF1zG)sjE{Vs)C=o*w?)*TXNn>ttt9`wb#-@`HK0etv~FCZ;V3L638{O znQ}Uy_t?X~XB)I>)+AYqcPi%=@J z4$P72MkBjmo_xe;?UD@XI68V89ToJWMHA^mpwp|^_xk$!6c?ZQSu7XE6KQyMFhEOd zVC+~0=*=T}QeENdoJ+Ld1gUU^mlwT3D{*ywjdHC|;F5Ue1Z$XF&EkvJ%F({4C4zWn7SoHkP4TA8+U*f)6#ezjP#`bPbHy^(`jn;n{4qD!5{)-TQmzX>E#b81~)Jlko$Lj2h-ygOfm z-yw!39+?*4D?eSLZoIsJEak$5=TS?*-|Q(@@cwo3f?!|-=~J&uWIRSKc*xmnp10Cu z0t2GJ3y!kpg%P+rW}OdqM=}$5AQ?(zQpj#>BvGJ|78{>4x8K4InX;P~saPLa%GHxB zl~pcnFP-zsQjLLrKP-AwTFl_>laW=O^a~b;z-FY%#l?33Q%4M?ejMKxeX@Q{f2#wN zLP~OmD3~K3e0%E-fZ;B>6Ql!0P;IK z;eq=1;=2QKcNnQ}W=Fts_!jHoy${0W*@X6Rvxl4S#_hVmgAlMQjO>i%ae;Kp_kV~1 zLO)q`BWrIyfgx~z8Z&P{>!D8&YPR}){UcQQB9e^n?^W0jfZwg{?Y%V^C$0=afkM`D zcdb3!;)IFTAqe~pv|)7DXRI~jl2NPH zTP8D8!`gVhk!#%jDSEJImJRcjI%SF*XuifrMQ!!nNJrW z2P9KOizLijEWBKQN}kPP94(PB@zQwzAgt<;$S>yCKGTCyRzF5qQVo}rtfLN z+L?n^UdpUS)H&?nHD6zwy$zw>oGQX!ZxSFM92~5*-sf$;UVgI5=Nd#flKc6lR1*s| z9i83A>7sRt)hH?<3K^6&CMm9jl}xo^ zWq+nzs_{jID!}W7m;L4+7^`>9*%ezQp>njssxE{a?iYOD2jzp_>C(o%*JyA=m%+f; zoNwZhp-%NHY(y)XaE}JeVvjByVJdi~lTB z^X44c{Am!ORU-_L0QVEZ1;F7$UOv9E-4%nw!&7(&jf@+9O^Qp&{TO9`z9Fzkximwh zCjtu@{!a~kMCqPz$@<*Q6FdsGN>)eng^1!}`k-g1zt(J5S9x3K8m59GBYUbzXNFt- zC}?p}?{E@UcJ<^3Li1*4{IOnUj+P&fvVo^THK) zxPC%~fz4(zOVB4zy+79!IBub4*Moe5{`Ia|EuJQnY7 z6?DHEQ>LY(Yd_cIiH=V7zgQn^BUZ>;nwvUU9v*oaG)3SWcy)Doe{*1?lfFbq1e)^3 zi(CHM4;RENtgL6bCnqN&>1j>Y%cGJh5)%34Ly4o0;;AStZf0?D*v(XLFUq6tx8eiq zT-TsCzk89PNcVaKb+}wlJeGB>)^!aPC_$a1dGZB^HxKtnpAu1N-}IC{ebynNXzKFB zuiJ~g@3on?5QBPq&jh9nHWJ`&OS4lI8cYau+{zi=BWZ)TaHr3zBC zPQPh^hi=Vw-^S8?uvu({p;!Njn=KaE{WYJqv#aZ|8GXC+V0^n zgn+#7tk~R5v!`ys!@_*?_(=jW8O`$oqu|S}Kp_c^FtWTuLK9rz_c*M~fNXPMwTo5z z_ma=M(Q4mvX9_9>)g3eiZnB$|+4W#F@|`g3&NN^a9^3wQy(yu4L&16L=^f&a)ykxS zqe73j^6*VmNUP8fh*Y;+C$%RYwZ=~kWzr)6LC1al%-L@Jw`mJolgUV~O0zj($SSq- z+L-b`-VVwH5R%>r3PK!K*Q9zq;l3ILot!)4nf@pPW2JNTpO4!8X_e@h4E|CTYu<4U zrg4SdHoAw~qZ6^U$`W(JLhHYiLnHARy6aetqLY%;e93tWUY(--!T3d&9M&hFBZeMz zHnowi*ZJo&P^u@Z?T`?8Ik7%_+&pHYWDcwDWLCpPa9-|_MNYaVW=r^Zg%bz%j^x^2 z#TF>a(bAblIaU00__p?V2Ikv0;Y>wg%6PjOx6|D(7xSs%+FtjB25SZq+zzOcc``6i z3KfQ60qRJuRNLf4H(}E}H~^BlXaYk*LMm67r>A@QEBf%l$}cjWp<`mf9WGV*1db}Akt0Eh}8;kF)U;R zYbU#&#Y~af^%c-EvAJG-1#}w`nwUpz?GEWwe5UWJqIh{(mx_cZz^7ff(sT?}v*A!V6MujI!_#BYnd&{PzucY6)D@^{cobaJgl@G!$X&;9+L?|q(gp65Ap z@4C6UIV(7Xcd!rZv9z*51UBG5oWjp#2JZCz-o;6rC^Ny3?7_SEt*n4a`g0S0P_G

U=z;awIV?h>?)-@ zaoC27I5=v64t8YJe~zE<*r*9IxQmbQQck-a_zrL3k71K^(nPCq1>X)EbO_#)F9_R( zH~MY-K1_lnr@lK(uo2ieMB6R+2Coj2AWv|uu?^>iUDhMfG(mU-wFXTWB#h(*?0#5f8w3e*mxXI{fVK$2^TK9eVC&f? zLD(rg5!loFL)!%5S>dR)i*^bUuE`6SXsaM$eLUV~LFm*i_;}>_OP%~SQTYmgZ}b+dTIpybg={fHts zo0cpV3@#7R9#7VJ4KC(4DNdb&{i7C5wl2Z0!Ru~5R-(ED&srYAc`XyXB>W$qFg1_G zOjf1fq;PWR$t_^YgUd*%R3W%u7_}>w7xZ~yE&MZf@+t%;Jvb$&RJ2S~eI7h4;tl0t z)V002e^1^@s6gwSa;00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru;sp%{8w3W%UV;Ds2xmz| zK~#9!?VQPirX|Ade*CdecRh$P7N{oiIQ1Q|pRARuExNcvnLsQXCLkx}=lwY+ed zslRkrs{X2$A%qb8Z&b$Z;v$x1LDzLymIceQV45bpULF7e&-3tly?Y;sVHlXt=KuiH zG{GXx}AFtO7uh)xUFbGi;5ex>ubWg_3 zG9(0ee(s{%?Lyb}^}|u$jA0-M0z^^7`T03`o_GDg^Z6W)kB=CS$Ix{hZoLS{aS#Ln z@pv4uSR8KN8#S!aXoP07iP>z1UAQ@qheIqDMJkn|TBe3!;OXfJyO z*(}20@NQsPmW79h7TWE9_Tp=Jo<}yD#o5`}#$)>ZKAO$u+9&PWlF4&qG8c}1%@&rf z>!{Ug7>!1IchHGM0=Zo7gFhpLpwVcc)9LK}{^4*K`FtLNAh?I6X&UbC?)LUJH$8B% zSOm+muq+GZa`_-H>isOgzP^Uf=c9*Z7zTd-{taE%4<5@%l7w_RjmO7Fyu7>|{QiO< z;McETpZd{HVcp%`VK^L8J4#8C5C{Yi38KLVsR8_@vI(2<+AP|5o%eG%F8mrZ6=ytn~U+Bxr%k}T<+pL~cd*(=eawZxW4@qm4w)_8JJL(`hUg3s{!*>8whp(|f&tBywgO08gh= z1OfqPSUk@|lEl3}EYI^$6y<0Ns3eKtd46NyyyC+WLTs0y$KyGO(s@ktdcAA0a??^I zgn%UkwrRju1iJ5NcH<<3fRJ~g!zSC5f&Vn48K)zxzoqy?GfqcXv)SygfJHM-mSF&v z_1Gu|i@ySv-8ebUyDY6Og?pdvMeM$oKfyFjvxOxHJQ(J68QSGXhGAel9{*@qL|awX?Lvx`GQzH+^hhMK@dV4V9EGy) zYTa)4)135u62FS>*|+>ivc2f@a}U$$&k7@^(;1$h?FG;Gio_aLAP}%sE(x*F>9l_o ztWKv5LM&VP=Djq;zL!Gc@r2`)wG#=yllA+3N7oXK#y$v+?8iTzIf`tpR?AgQ?-b26 zO|)7q+hdlZvHz1B#$qu?XW#Gl&~ATE z|J81{(eK+kbxD#wmAXGoq%)bUtr!LX&1RFj#D6L_91hWJHf@jLI1ag7j$U=}`+Z2I zQjU^FtycRsdE?T3)EuRlbUMAUJhxGzNu^Sb%W*!R(3F^YHI%3db26WtJhB}q^Q^HR6%qmlc6diQ+3j((`m$F&h0&&P6v%f<0L@Q_M*n) zah#u@Q(cpK+riaUo{E0E-99?7Mx#L$2a2NTsw>FV9t}lNsCvea35*t0I4ruZg1dsn zvMloXJXMYD7{F*j`FuVU3RkZ8^SIvqBM1UyS*EH~x7+Q5IsJOQP6dkRdC0Q-rIrNd zt6S(aO+&eS2VHmWLjJSea37#(oVg(IC>F23TxDmr*>AW6R;KEg7ext*qJU+0*N|pe z1eHn!!{H0niYOL~TeTxF+iuR(G!2zXg(^oBMe!9Fy8}?xE2trrk5Uw6>%NVxpW4lX z42MIgs=Bp!XqqNeRXr%Eoy@G2&1N%HDix|F83_1MD9B$LfTn4vR#mFvi69726veSF zda#-GChPbi%L-NZ@pL+as;aK#9Zl0-`{(IE$@1<%?Q~|npJC;4nQE^N&-1vtx}pkn zN23vy%(7Z~vMlcu)Lvk%bgpW(O4TF5Fbrf_wk`4w1_P+7O4UOZ3WcC33OJ72^|1B> zYh|~$w^aKDmwom*BogsYlJhQ_%?6dg7!HR~DwXyY&+Qc!Aq3@enOdvQWHKM*?5C$E zJUl#5og0ZnAj|SzHWnNN)(Tj?UZ?JrSOIebYTp^c!C<*fNmh=Zb~ERpKphm88&!bT zE-o(izWd^6uy!fkh=1U+QvN|4=yh+9XCh)7+9-3v0AOV>W1bx z4vM0<-qiW+VXdO_YPD+H{Q5pBzq-2G+g7@hhqcW2^w-3AYLj>eUh~f5?jvGO;h&A^ z`W&iS`4m_%7(}5UA05=mzyg+H-k;TDNs>@37I7-Zp3H-=EQ-bAx;80_;z@xzshRaY oJ$inAMl2TFYo7NbV4br43!qLwgv6yf$N&HU07*qoM6N<$g2iG@Hvj+t literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/icon_sd.png b/sys.py/gameshell/icons/icon_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..555d2d9ffe3908568b3186d45d7ac0589975a428 GIT binary patch literal 2146 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 literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/needwifi_bg.png b/sys.py/gameshell/icons/needwifi_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6b42ba95a50abc37929dea0cdc427ead41ae6c59 GIT binary patch literal 6672 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 nfyhA;vr}sQOY=?eh0p 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 literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/scale.png b/sys.py/gameshell/icons/scale.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e2dd46d70ff65c2b824027d612a701391b60e8 GIT binary patch literal 1886 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 JPDHLkV1g&T$Pxem literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/sys.png b/sys.py/gameshell/icons/sys.png new file mode 100644 index 0000000000000000000000000000000000000000..aac76d346bae8f62f4848d81cc13e79a4f966699 GIT binary patch literal 427 zcmeAS@N?(olHy`uVBq!ia0vp^LO`s-!2%@po-E1%QY`6?zK#rxZ3_%vOp6EdnUcKS zT^Rni_n+AhRvmYwaY} zcTtsw?+;81P}q4uxwP74>(ZWrSt)nzLVDSJxL1c?6Z#pn>uQXWs?HXBz00re>z&Aa zFBft7&7npvb3TSIoDut;Jqe#uBYI=S{G7E@LVjue^m-}ZAIx#|+1X!lPai(wTxk8V z`a}g&%jQTU9jj-zfL>KCag8WRNi0dVN-jzTQVd20h8DU8rn-iPA%=!l1_oA!CfWuT zKt|@aS!O62a`RI%(<-4FOmq#6Lk!HVOpL7z%pe*{XC&wVH86O(`njxgN@xNANfe+7 literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/icons/vol.png b/sys.py/gameshell/icons/vol.png new file mode 100644 index 0000000000000000000000000000000000000000..49b56fea5cda219f3c3023e49811a9859c825f2b GIT binary patch literal 11833 zcmXwf1z1&G6Ye=QNS7ihAOa%NB3%N~B_JuKASvA?NSBmIi3q5)fOL1mM@e^ybcp1g z^WS@)XB$Tkd#}A_*326drlulCfOi`Yfj|&Em6y?gKbzoJAr2P&zV_4MEds%Scq${Q z<({@(@8PVqnsj|B${+LenMFa6lKBfda?Dm@W(hQ0llPK4uj_8=c`X#}bZGBN6<3w$ zm#UXqrPh`gD;VF*w|zIpD8nEr6C4|gl){l`i1jGa()h;4Kd~EA=VHX8HTL-4H?6zR zGAn-Qj(Q9@We8RL<^A(Y+>1;lZzPQPP9S+wBQ^2Tb*EkBc`dG_oIDaOs5kjxc41-v zd_Rtm6do~Rrs@s4PO%ZDB=MDx!%&ii{%d*KArMj$aYynbP zO?@f6oi+a?r7iJRm|z8cFL_}p<$4eJZWF)-9}Bea^YHR=+fMNzTJ8P!-9`4?MJ^6{ z9}aCDo6TN)y2FQdiZgMM!GOb>sNf)YSv+MU!1Uk&!3|0dguhANQ_d;_&LdCJ*VfkD zUMGLJt-ZKlujtK6bz@UgZ&VwI{c%xUQ=QOynxpaWyN9HzB_1Ev-al{Iza$vQkX9tT zd(Q*BMMikYErHaisVTjnkP!5Z%!9VJHVI}@kNSyC=fKuQ2@cBhniNSzd89XfOlRxg zc|TQ7=R)oKOG`^?YHElSrL}z{xEp73L{LyrspV@mQqZ;wnAhm4#*Df+`-%<;eFZH6iLPV&U^E8fLN+^&!>nvFHZ3i3vh0;~mRh7m+1uzLa?40uA|g3oUomn# zObCS_%|I+0acX}@PvU(NGP317Z7d?VH#%N0n)O7{6D_S#|BU%3mOZ?#LF7%UUFJNF z%sACTr!lc;rzF(|(RFnqGBPsw!lXfaqVQw9adC0Fm3Fjn@4lC1jj3eL#937m0g~3% z_c72UyMFU_1d%hysF8g{3&wd3S% z48ofh9Z;uY(Sd;&r;C^kw!}stHBx}YbR|fj#d3+%6{7UE8394d9u{P=!-r&r?=Z%N zUO2HS$lv(@X@v(*{-DINaqM{uA8X_I?9L+oWw1PRpbVp=uI?=iG~_Z4d9#8%Qenq+B*gNI23t!@OV{w7pjT3OTsV0nxP{sgb8|*VCns1+s;W1`WYFNM zHc(;1P%I(U@V_R4gsRX}#=DXU=v`&x;J}9vn3$Mc*{L*<9N{fE2!7J|_G#FX<*1tj zGIwv!K}J@VFie-W52FI@>)7kBTI{e$zeg5*@Ku}}H?nEc?LLzw;C&i<4Y`D@Hxg&2 z8?;p6IhVa+6D^F}v@pO(FD={^-qHGbNY@h-Xc0))=XlQJV z|2Og#d`pIr1YRoTUwcK?DyeI7p>Ik5+%cI*gO?2)ezz(yUE28+K0ZENxA2Ev zS8%2FfB7#GgEHbtG*mp>+5OR6JN^&N)cl~38>y=PE4ubID ztdY&3>FFC~lOjv%QJu?o#KhM{O0*D*bKSqm~=xDmwv6qazCKRZ%R9YIh&p2Trp4_`?ZgaiwKlb{Gswz=UZLJC4 zZL0gIEHE`)id|?Ri^9!A5Qd-^78Y)7Y`kz54`~fB;0%$<@|R(xS{;v~Rmmfpot0u=OC;Kd{+FW@6TDOXiSo>$;o%>LawW!^S9{&;9zP+t%3` zqachH{H7S86l}~G#~>!=tF`tI`b|YeMU}%MTEqE{u-3C@S@rel!wF_wQdz{MrKKNu zUlD2-=mtQ~GCf%7X425DosAwCP$~cU^S=hS&uq{f!BEsxq zM#6N49ThX!2B@8@UO0hGU!KMjQ&8}Kj>`D{ot;M1ll-1anuN48`m(32f`S5`fbCS! z_H?D$c)|0?-z9p5mUecsZf=hX&zF{$NvNrNH&O!HsU9am=Ne2B$G$< zqMkwM*n|tC-!wOV|28!kv?QaV3L~HvcrlnE`a#5lXZxI%5PNEB%JM<1Vfs^NXTI%q zOwwf)WSYvo-&dX@4GU}DnXN6gp5T;? zc$ugW3~$+!8UhD2EG%q!%2Qy6{8LU2CN%kkUd0>@$S+=36G68fnv?D6kb?t9*}t_5 zhyOyOqu=Rrz=ajl3H=Fr90QXQ6t=Hg-$~>?d=U2&oAW)lEzqk7KdFaYwY0JM^2eoT zc|Z33dmg(-1f7Yf8Uxv<$w^N~7rnA#E=x%U(57p=Hz}4e}i+_ixzTTH1 znp&O>YmsLrh2E=ygsu+}fNN@cdU$Foyr)NDrq+#Ub#>L!%F1N)9qKOjjh6!3=Kv94 zKiKXEsIfv%JgX8361O97vgBK8ev6&pmrzo|rIaB;1%>>i43fCS>#vA3`MQ#JuG_Oho7&NH0qv zKc=R#30LA`pnb{9XMa{lMrVS+rPqk z;&WBit@FRTf6rzi!CiM|!c@gC6UKQ>&CE!ssGfc+7`Bvo_3B>X+2vD-7=*J@Mm&qM zl#&wu_A44;SN!RpwugFlzem3bI{?Is`nX_1k#Osliwp`kdk!td|iz3aZ2lU$di-{>n^1f^l9 zg$R+7l2YaUk7vBdaI4}^#KQ)8tErX1{+CQ_Y+>)8PP(vmPuzJW`_})N|MVNm+%Afrg@nsPZlAK+>gg}M3Y7~oqv^>paTkJn|MHs z!)5g!v%;=k_HbkbaXj)H0!~mu+Kj;!tFyERw7@^VN?Y^wTZMP)-U5`&)SeA!!KU~2 z_xqRZx38NEIJ)tCcGr?NR-KhRLAl=6))py_hmnSAp3bhW8lG>|)zu@@rsv$32@(<# z+&K>Cyml93{4SWbYtX~$z5bd)Na%V|RKdRCAIqu`ZlHvL0Zq;~HDhCAiE|Ys@+$&* z3fJu@quw{w2D0l~iV6yQ!*cW<9EVfZ#U@aq%C`t*jQlQLE)K=7%Q|_miRfgWKc`w1 zGTeXr6<`ONCpiu)rh_2O#?DSIA9E)(8V2H_@AH;?rV~YmbZ%;o;MCjYiS7ux?=fU} z?L4lW=zq!Ux;^z|>qVpiXCApolW8f&5i7A89v~RjYGL^t4QRqs$9`vy%wO`i2AZ-G(bYwF~uaNG|sjM-H z+BqVIPV~UDD;RnlKY!xSMlU%z>|CXkrEwm>eQFvSftWWahrYXssB3G#D=6To9Onnj z3hAnup(m8C!pFx4-OP(aju_*8d_4NWj6_o;!m~c({c2|-dIabB$@ZaT()onn(Pj7ErQj+X9vG3W?@}xXHJ)3CLa6?I8 zuhgdaNFv%J`!bAujuWTPo^w#1?9S7Zw-6nv9?$vwHLP@Ui;{hNeK~)fV?ZxStj0=i zB!|=?{3*f2KrWFt;t{p*C)`B!W`$nVp!oI7_VMx7gjstyMfn!(Ae8u??Ao?hnLe_L zy5Ce339!(C2CRB^qBY+4zyILdqf>7&CQK9$gnBhQUJWI7%0&gkb$4$2q)$dulZ?}# zN}`%Gkbxn-yDn*KYs-v0ZXo&85NWV(QcprhH?UDPt7Gi5dNTF-NkoZxH!13;Gki{- z#|-#5=SoUk?@G5*hz{|*IWvUgn>}F?*Sg5{`;z+h?Y&ka@v8c(O26~U@~t#^_p_^; z4~+#${`U zZf19DtF?08S24cOnFSkDKor~w(Ar;?u{|ik${yKtI6twioH#r@w3>KKvhQ4eI9{k< z9+b#{6WVTCyc#KM)+E^V?-}wdelS{qzxwY1>PL1}v$~2(N^L;VEa7KT9y{gg4-TB1 zxB}IGtL;M?*0#mhWyu$~iK+}+nl^H$^_DdhU6&5jSnEnWti*wJu|EcC=|%JT^KyR20)=|B@#K zt>wv-J>Cm})cOWQfJBoZMXd0ybg6FS(|8u{?@ph2pwv~4Q?nSQF~)Um3Bfi+3YJ}3 zEmX(NBx z{8asK8>6L!IyCacE9>BVa{FZA8N%LpMh6f04qD3@vAixvlKOl~4$oNm^tP{C_~G>J zCh7)-pSdHOmUn*$&6yp!h!u^_>8rE0-_>_Q^sJ2f%0~N5>b~lNLo0$BI6WZ)5M-=c#@SztDJ#4v! z^EmkOGeWDO>Y*HNjo1zutt#?u%F>_<9c`cn(r&J9bBsb*mn%i7%J=f@>xAMni+$u{Y-Rsb+F>H}b$Y70qLst$fl( z%q0oxduHI=?W4U#Y3NVw_gwS~wTtn%NMCghx7h!dYu6u2zFGp(k?DO9Hu#&K$oruC zp4eg3XN`vxRD1lsWoYXA$Mj)nYAO13;=UpbX5%rK?1^}tG4y}+qBqHd!xL?p*F|L` z{IwD6*z2>B?Ns^SzTwJ6)0k*oyRSpPmrzuUm~d1YP({T${iY;wvoIzjPT+st1#v+G zpFT<*kEn|0B&5G&%!0o3D(FjUIOWFmY~41;;lB$AwExs_TSJ3bhyIb4lls{B_|p8< z_WUhTv$`LC*S;t;%-+kKmpp8F^tyx46LVqT-N+aihee&4;=RvDA@sht_;yGD+_eCD zWlMfV`r(%)pC@hgKW|?N8r9=ew311i;$bS_0L!`u&ZJh;HUfX9DtdQ|sv9~M9Ts-4 zuXe4XzAKTw*L-Y1#v1qdA{W6y*_+4avLWkt|G|THz@7se&zFNmy-pU^GyQU1VrnsN z@1VG2If5`@gT_}A5y-&G=HL9{MLFRM(yNQ60CYj8HT>=K4)i>gbQA`WI2TiWBOM|Q zJ!7_s90wzka-yNB_h!2UuFU4GQx!cX&mL8HSw;n4mNqkE+V(<^d-o2@X8LDT z9JeHA@-k#buEE^PGIczULB6HRhkLbXYES(ob9AVbKn(*ClBy!Vrsj5ab#;PraDal3 zkEnK`UNAIo-WqX!ev0^es<{S6*~9MX*Kk}r9@BdV%*V)Rl=7zbzm!dVHYj#pFl{}j zR5CU;hC=a$9Y|Z(kG%7I4DD8{X0qUf{lzr zM>fq@%9hzkGNiI3R8*on&r$sD;=|yzD z@$coCeZ|rD1buZt50JJhvg5jCIcRp1(;yUpn@RDevyjc?56t4agYZrv;5l*0$ssY~ zS0Bd9F59!DLt^10Y*EZA3U-S8opAy!q}51OQL&YGP(=D)wWz45k(=MQt8ufic?!ga zYJu$Cj5>v!&(De_7@F*T4^vHwkB^TR8?VnB_ZLIxO`+q{ZE@eiKh5(yy9XE)Jv}|W ze2PmOK&5GCG_Cu;)#Q3k87D`_y{q%x<*qm;JF>)!wMUIvHu;sh90~Xo0RopuG#Ml_ zDK1D^(CoLjbG92BtVUQWCyI>f!yI$LwsyIh#D2;3u7g-0?G5p9Z1cs#A?c2&M zWZO6|oz@51e~;zQ8u@J%R!R_KgolUMMPne;baX=IFL&H@%U%)ykFlz-D6jTBWP%~~Io&99OGjr8@w%uW=F@i})*6LyQ%_LA+Xc3R)l z@jKSht#jvwWwFkdy;+v^KJ0%4hyS#d{tAf1xJhrRF2_ax`~|`4PmudG4kqvbr+nxy z?!1$RTtSXcO)0S1m;gjiP`JD539|B!A3tW!ngL%M)VdO=iv43oEUYFQcR)(8P7om! zlR2Ll8PN&Y{PG8qr}tMQD>oMlb$&tDwbIWq^8IJweKahG@CUNbVNf+>d+!Huzl+o5 zm;{4FxI5694VQiZ#IbR4BwU0JF=T3}=B1{ox$MFlJh%?(dQ_5l_vT1msqdv{<-|=6 zT`md9hKmD56n{kN3Yo~GS4NqlUILU4^nxsUlT3iJ#de#Qr!@kz2g?3EKYt&<+ovyI zFrGboR(^8!a@+oHk2!Kl#XoJ~(pv!%1g_pX);jCQ>D2iFFr zw8+VYOgI7*kXKOnY@s$-`cYd*_ z$c@!+rrx`J<(Fo5P%BQxnAJ7VmD&{c(#&Es)kez70p*9Pnr+xzNx!r1I^t>3Ot z$6JgZl#UPl)|5HxBA!Q~?>Bj4;Sv6n&77y+4V|;yoghJESwwp;h21N8HAq)E0aV)e ze0rXcn`mdInryfJFWu_;Y5d9fODdt&IIImTlX483D^2l*H3DiUo?SA5d-87|5Swd!L3&A)B4#)KYE}Db#jXR zAl`_&;>UoKGBi3W^UcQG+`Q@ZvX+T~0)!C;7M6GaT{M7{my}Hdjz}lH-=ET*jt*HH z0JBX*vm+zY|ER83_FV<|`3e5fy)`MDJdL?NCF;Q*1wVtW^D;6jDn~0%99JF_;SaK2 z&y75&bb6BIlu#S5F9xrVbk{)S(*0>e1^AbsseXc4k{FdNa~|uoJ(@VVeINP%Cp5uf zpJgw}Pp`C}@AzTSTjyB@>HXoyAW`}Ni?>EqU>a$G&bK3q`1$U0fCcLIY3|g39vd?~Wm8*4#o99C7lo zv9bQ3JDM)G1b=vBPnWBj84ZF%-PPHaopd$oz=E*ZoAltYM*94G{+cd?h(33e4%it; zmv9M1#qDki=7yE6+unWeRL>*e`hl#~Y1Tqqk9qdp1E z(_(K|d{`d@%(<<--J~ahO^rRVwT7tH2^$p+U{~rI8cWYkCkplDCR@iW`94=yr@Y@I z!w5J%J$-ZMLc;$(VyKZ}eA78W$`%ym*ez^!C`mMs;h-TkgVr;XWDO7Qj=wh>&DrXs zLW}_(2V6WnJcuLx6qQUd(E>C$lgn?3#6B?#?(_g_OB3<M#|tWkdl(J=FQsJ+UB^pv+(ecLZ#^H>-(a0Ef0!=Ypp+kNv45O z`5Yh6b57S%?TJZ97Ap#7?c@Lzto~903IU>6LJt-mumy?hBmeKaF<{~Wbu4^)1mX#b z%^n;a+^UFqeJWwUMfQE`X0GU;kNnnZT3SJnjl~vl;%?u*&HB)D(ncUb!9qgDEBmbu z=AK2QuMoVErfZ(dv4L~TH!usA+rr3x)p_u4{~X@DD;I85{MU2JTJR?*cx3lf)TSQ0 z3DTg%Q=7B9IB1|sBHexk(hEjw1EuKt?}6;Ht_3!E+wS* z{BaewNUgX5Tx+>A#tcGTK6T=E{nt;o;xz>!tx!;SI9}x{PN0LZ@*AOaIyry%Oe5rs z0|@E#Y^&I+VmDF8s2N%zYigpGr>CVdQ=&r7e6&!s*3JxPpqH1|e(%G^CUYK?iuJ4N zP4=|Jx5UK64`Mz-;HT*6>%Tc%QwB_nGFu~VKBTL7LK4y{38#ln#9iWP33O(t^o1U5 zlEkNf{jS}>0bZoPTBK+75Jr=D`V<%RbB5$zZnqr+4x_qT5T_5^W>}h>hediAis{G%*dq>hs>owotb3S7j35#}j7raePJq+g_1bpVZcjKHyLCgn_7fe-= z{Wdbh874{!1$%R2b2C6C4+WYN6jr~c<5J*Y0J=!$0M{*?#$KWB3{wg_E)3~^}01SncmVrREnR3H{ z2QWA^Hy8c9>}6c8f>>q@xB(g-&~5be^tfDpkE(l_fSW->*lDXv^r;a$Iwpqx4dc6e znck$}A%XoM4H0U(g++G{JWhbZcw(1FnjFu61icZ3li*rAyX>`k_)lI{^>0MJG$a;O zZY&s3NKq1Cz~LdQKKzc5@Ka???nPBUcnuKoziLoQY%QWQ$4 z)Jj1Z0+vTeE7kZ^t7$2eatXF0MhbX`q=&}QKz^A-L}-ZUL~il+#@qum1}aw+0PH(g&gIcA`KGR z$?@RyWEU3`0h0y{69{&T4C9^P#`1FV+fnakqM|}W{k8L-fJ6yS2nddHE_rGkl+vu^ zEq0ZyuPfdQm+bHFi-k#nv=|H&#SEGX4)iZCFJZph{U2B+z-I00>S_|dS{7g0TWkgW zx%I1^MD=H9hiU^|4oYCS@RtMf4Dq1ryiDH~c)RB1eXzhyDyKo&lE(yP-Hh4IZXHWm zSs5jYdyxHKGd&q*9t2%}lY{HU`yy^~axx6+wFGB!{8RR|!OR%w)8H+GSBS4GAA<#@ z7EI}ty2Q@qf8HiZA3lWooS!U4J(`nv^M)HLS_B+yF#Z0?P{ER34y1+k^(n2auQNS* zG?AxCOc@ajdKN1=9{eZt7Fcg)2tN&no5VGzhoa&QD1ZP}uv7crZA}ytRTbyuVS}j& zNOPqnIee!28KT}O=HtiPP=%$7Ct4RB{M$_>S;-mM*l>l?2|?m!<={XGfb#RZkDb;& z0eJ-G)dV&e_{4YOyHO%Nm{_RR<`TtH^fQWIgpLUOLsnK+36OQ!+1VYS0va^>(z@-; zNP=s4H=^eS_}*XzMWO(}T%89B0IUJNTPt;e!HEwnWz;G=IDv@d-u7~J^ECHtE7`33hGvmt(_Hdj$2ADZeA>g%PgeFvm5waemzrP>sly?Bm9?g5d zb=evR50~-oT}(JD9nfm`K{-)M6(B%#p>x*P*8{ny)*;l?itAnN8W@mubrtaUEmj7d z7or3f>Sr(lZ%1M26;gtu3nM50ccuXX278DaIRVZAS}Al-?j3Jy*>K@%GQ42(BQY3& zFlTlYN+-s_0Q*u3B0)tqo;u^9WeYP2U}i%lCECQ)(9np5G6H7<%=|?#VNe?fMFFUK zsps);q-f?=GfR9}sjXKXV4Se7F8Pkh*MStOj@VUyQ%uVyVeN1A3Rx(c;Ld3wFnJWn zFf$bd-5(SKm@POvJKNi?oPRHz=d{*$$F@A?(bTh7p~O3TEN*ikVKQcAWd&w+THye}+)nSR6nmmn_V5CbzOKPR7_eDFOy~FJ4^@OwFa`Y0WaUWgIV)IsX3}+IU4+$>Dm@NLI7ukKcHZYNEhsVs;WiRoncXOKNuL} z5xk!{7}Ebz8ca3-C3v62rCOnt!W0OMU4Y&oCyQlmZ;u_)iYwGB8ydH)ykj?8g9&3O zu2m9#>^e@k>Zq~9f-p1K!2FMH># zetI%(zrQie#1ex?64;hHQ~mYp*CwdHI3RZi1!0IBk80X&nb*<6_?ZERaRUuyghf+P z8o)k4c5qxldzN<^M@=^=5SzBSzpn*A4HX$n{OXiRiG>VIdwAjXH}||`ykeRp#C0HaAz-mKR(SaA5D>zd3isNHYaMggY$6g(2y=lij zppD3L^JM0=R5F*@FSdZxdS~6ytzL(*FDtUDU{E zUWK_r>USK0AWmqpkNJn*fc~gma4UN_6v9M6t=YM{#hsA?dc)?SxS(9g)-}8udYy?O zvaHVHg`33>5!XMzzE-%2@aV41bc3T9q>y9Eq50*08)SOZ@;;Ufh9-L=j^Y%Vp6+?nu`11j;9<20cAJT?w*qra;!?D|A>3XoI?ovYpLe zy;?rsZM-LL+~EC~)3An*x$krDAN}XgA4_n*GpqXHyhC4HwW7eD_&$Jerki1io^ExY z{5gzq0JTJdy!U2%ieE!aUr!I5NFE6z!&oMNl{_ec9b~E^0fB*NFsc!sxW2Z=$i##W zrmxyJEpxu0&TK}o^jMo9&?%1|m!29C0 z`QP3Df;_;Tf=LLgCYPH<#GB5yqHu_oag#;%0d@0W<0ncYJ_y*}{!vz@uBPS>1QCFW zbdCmUMhadVrcR##kAgd*Xw~dZdB|s25lFxyhM7r8SSd=f8BdNW@q&6+-;1XKq~R|r zEF=JBH<{B=3cCKk_726J!e&@6&-zY##7*oAFPJX6Be$zvWPppHi`PQGROdIqaP(9+ z{$IdeK(ht!Q(aeAo`s+97XE@`HH`2ehcdb4*b_}R|I2C3z`j+sw*gDj_?95TyLCog ztjX}d*$w}zg)$H^-B%NJu)MDXp)z42yaB+|3C9&ToZ%r#+;-=N?RJ8o381I|fI?K} zpqV2tX-l^j67$e?Bgvb_YO7VIXR4h#ZJ$p(R?^A6`L-D)NjOx`yqP|)b@%4319Xj$ zzS+nLoDnp$0C>z|`%Pn-AKBJiw@}9UqN?8+8RErAuCkcjq$wJAezTIK0QMtAQaxY&H3*95j#7cLJldcwtcqNt$F_dm-ZZ`YIfh z%czo7@5vucEBp=sgDKQ)l+FZ$FAgVvp8tpjc2Rj_79|}0`ojsY(GKvc6y6Rse39vQ z_4o?Rbd7U3k!o7Ha9u>_|N(> zMZM8(Uso4{Ar;o{4gB2f>{gGx1<8Yq&g+w%*{$(`-2tDLo!9oIx-&fs4hMoW75*ymLPi^Z{>VTRhA|VXC-q)4NKStKF|z{Zx6KQ~=`+VR mI@MvX*=cS6H(~nEFSq%DKXtC<1dN0uo<32LDUx~-_?LNExZ!?T2rdSj-*kT=J*_nz%0JyFTLWltXhVgFuJ*y&wKv|YAI3eU-M@3PnrfIYW&-0KZ z$(!#-m%MG;VdbNk7W5~}vcNe9rSvQfz{!r{IEL^0uk$?5$g=D{NJ@!u93g}NV+?7U zf^!Zjp+czDm0WEz5$( z<8cYP9L=`vOUOr{AP7DJX-6|n^U!b<(|+2n>(n$&S_7pNj^k*3U(pkN>QPoTUum}h O0000~}U&Kt-oLT^vI! zde>gs=*Q$JaO|Tz3%Apom$jVCVq$jOowBS;kBSQUWK^ylzi8fv+k0|D~7ZiX$rebs zeppYpIOrXvzuM|lR?Fs1jS>O;<_i}}T#)#?Yr%S-y3k9vR|hl9dSJQyfA8-JyWM`V Y)=H>mtbVbg5a=xiPgg&ebxsLQ0FJAHU;qFB literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/titlebar_icons/ic_volume_up_black.png b/sys.py/gameshell/titlebar_icons/ic_volume_up_black.png new file mode 100644 index 0000000000000000000000000000000000000000..b1a5d343a55025b83177fa9d55e9fe359a5c2621 GIT binary patch literal 353 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 literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/titlebar_icons/without_charging.png b/sys.py/gameshell/titlebar_icons/without_charging.png new file mode 100644 index 0000000000000000000000000000000000000000..ae46cd2559acea980dc756d5495c4329583ecfca GIT binary patch literal 627 zcmeAS@N?(olHy`uVBq!ia0vp^LO{HTgBeIx-^vvLQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIEp4?RnNy@Qjy;N*~{wP0?n3Z&C;4+*Js6kjKi2?rpGdq z6vfDvH_~VB<&;O}DjqMDZQPUj$>Njx41Wy=t|bcvG*v|ICJKtTtL<60E#ORqj)1q& zvA|gef}A9S%1lnlZuR7+eV zN>UO_QmvAUQh^kMk%6Iwu7Rnpp<#%jft885m8pfcfrXWU!P4{o$51rn=BH$)RpQog TWzzR5paup{S3j3^P6-y9Ie@{{ z(cs^I{p(YuC$Y)huzNN0rUY+pd%Wp_w(Oh{Cs0sU7r^J{Kf0@_vF`Y28KR&LDORH z)GuH6*Z=>!^ZUNrkKb*IaTC9D@wM&iSHI8M)z1Sf`p_G<_0P8b^JAL=k{OvI#UAwb z)UCF@eXnTG>B{)pYd^1F&n&#(&dQMz*NRa z>;4wv7C5nq;r@#^+qYD|-(EWX|MS_o`4{)S-t}GVW&Pg&^A9kwY|=TvIZyYX|L0Zd zt&6`ue__05bNB1@>n;oJkq2synx6Xe%umLGi9nk;6(*=QI80+;iewt)&?OA6@$0!P WebL*0@ewfRGkCiCxvXdz`1)5rKR>_vw(NF<_JOsQ*4C@*_Q&Vn z+p}`MeSO%~{fuwER&Cw9Iapg;`|Ub`2h(I_WUl=BRmIEJ9JONA{qm|JBI2IJ^HJ9y;sl=xo`m*RayU)^_c3|M_9%xw*c78Z@}MxwmfH z7IydQ)vMdCGZ~bwUcLJ2&p$suSFXRFU9HOyY%0OCO1sWsQQZ3Qdmq@32stQBV_@3I zBG94Q;Go0ElEkSX;LXq&!Nf86q)OMnWi^<+ZQHh6affkGIdy?KEVB`E1R(ix)3e-tJytTmE;?{rNoeg-h%VCUZi3o!|Vh zzHNJ3cy71;zNIPCVzxa~`>*zC1}GFx+(@hZwR+ayuZGqv6E>dlj(L_kcgtx@2dSMp z8jH^@-734Vx0m<%?;py-3c{H}VjmN)aVl(I_Lft?Z6oWmTP=N+sZ8J?JuPN&A@{!Jh3eVL5%C(hjd)kqe@Av?Kz zk{9PSRNb6pgKQB3+^J~|;h_f27baLs0 z%6vYU#5qQzu5JYrGtYo^(hHaE*Dv!q#Dbijo+@z^A75^_LzC&m)(*kXXT&66sn3%A z#GS&viL;d&Os;+T7Gz$Um~!WOp<`R&O(tffM7op1Df#k0qeZiKHEPAUGR(AI_pK;6 zrxBiPiyLCPi}TMIGp4-j&@(@ZFdv$_*D+l_@!|OSw)Kb3oxXoK)d!OFZvsVia*}ha zcVRdwrI6vMJ-U%qVl^|!8+pPxUwynOrHuM9qC-@VI|m6HowzUo}{ z-nh4K-ds6#%Io%PrX8W|?Ce|j?Tc$ZxIk>xtM!{VZ~pb;W3sNE-nGT<{i`m&+_KlL zLAtc8EbRTgy{mzB!L^eoJwvbB3qSZUdD`^puWxQn-@0p8l()BcDNupgHLikO`?^0X s*2nL^_U~Wa*WZ7wY;69#{kWeoC-bs*VojkhuvTO6boFyt=akR{02;XX5C8xG literal 0 HcmV?d00001 diff --git a/sys.py/gameshell/wallpaper/seeyou.png b/sys.py/gameshell/wallpaper/seeyou.png new file mode 100644 index 0000000000000000000000000000000000000000..c5f62b7e4036db9171bdbbc19d3185bd2d1b9b37 GIT binary patch literal 2322 zcmcImYc!kL8crhBjG=WIw1NaJGegnN)V)PVh0$rMtBtfO7^m(RD(9^l2mdK*GOH01n1K-YtEW={+;>ry?eiFe|taQ`#k%7zRYuH zQA!G03Lp?j$<@W_JO~7K0LBY2dEkjZ(i#E+!QQ$$IbMh>og1Zvm0Z=Niv^YSPB$CB z`4apye6%rT-_#!Dn+wxJdLFO#DQ2YI_c(W7^W3wHMv%?ZSbA>lo}phdFIZQFJyw&# zk>zKCB4!7PT+XDV1cv`-p7Z*QooL1?JL5oCMz8&rU44B$|IH&$@!Np%(+^%NkZh)x z3ABa=O*F+Wv#hLab(!P6CEj$F>CpY39Q01!xIWSpWrV?CzU@{pw@mFo#>B+%TSRSH z0)Zf7)t1!b|CR0u!e^G@ToYS8dg+OT6_a^xD|~}!k1^m6hqtX6w#nYJ`l7(yqsud# ziqS9z075$(ZW*SSEE6-ZUS3B|XwpxWQYc*>-bw1lo}Qlky_L1i&04^SR@>BciJ(DB z8y*??q06jYb}ZDQgc<@}d@OIK( zBNE`u3Hk(?TL8io8Vp6k73HZY7c|KDAy0Hi_lC8N zwe=S#P7xqj-_fv!C0O^5<>B$b1cgFTQ9e1cM3x#|VP=`Yo{IjK+@T$9Qzl#{P#N1B z%r7jwau0g(TY|)N-~Rn>WJj3KxKjG_xV87x?;>1*m2qNznypNI%+fK~$4a}aqr(wM zV!ge|b#--t7A2d%VwDu-Q?0D6Gs7C-z=1if>IS6aeKX6!vC+YGm&IZR0`=f^@ry~R zfNaly_l_PVEc^uJGw~~D=3iVcmnw>*T`ys2t;qtsajPBjR~Rf^BgkS$VN4`Wpt3oBlq|BTcc6VIdqeOLnuCQ-#W82 z%{s|VG;nQ%Ag2?!nJW^BU~ae}&G68t;L5`NI+|7+nOmpdeGdTU*>djs=cx@ zGE!6J(5n8v&|E%{dK?&*0{U3=o!b8q;&qW*Qyr;$si?xH1j+nu-5a+a?+|2QzIBW+ zo3y&%+u=IlmyjSugxji4UIhf8>VP}~h%MMId2=MIhPK4n*klQhJ96ZrcFDch5mC*q z7>j4{CFSSOA4U|f>GRa$x3vY$y?-%YnpL%#m~TWnLp8ax>Ps zJ2!WYBV9$pk#{10e4MfW;$c#^*OZd-O98CA8>Vx8TfW->Q13bm#G<4tZ-nicn$B3F z#Ya#(*#1W_9iLVA`At>v`XSPl;#GGKoT46$?Y$y-21^!0U`-zY{J-yj2YpcL;o+RhA=hHC4@oo>rbnmNujwzkqviPkx=FztUL_+=bFIsiktQ<4m z$!x195levmp0Mz)V{6NHSd3p1CW^SNDS3|rAvt{}H)Rddn3wR9$+wNr zlzfVu&lu%ol9sW(Y~dTgI}0d#e0Q+Ws`)L}qQP`dZn}QwPER!R7}# zfin>j5s!?k(Nqz0> sys.stderr,\ + _("Can't connect to wicd daemon,trying to start it automatically...") + else: + bus = dbusmanager.get_bus() + dbus_ifaces = dbusmanager.get_dbus_ifaces() + daemon = dbus_ifaces["daemon"] ## @dbus.service.method('org.wicd.daemon') + wireless = dbus_ifaces["wireless"] ## @dbus.service.method('org.wicd.daemon.wireless') + wired = dbus_ifaces["wired"] ## @ + + if not daemon: + print("Error connecting to wicd via D-Bus") + + + return True + + +def wifi_strength(): + fast = not daemon.NeedsExternalCalls() + if not fast: + iwconfig = wireless.GetIwconfig() + else: + iwconfig = '' + + if daemon.GetSignalDisplayType() == 0: + strength = wireless.GetCurrentSignalStrength(iwconfig) + else: + strength = wireless.GetCurrentDBMStrength(iwconfig) + + return strength + + +def get_wifi_ip(): + if wireless == None: + return None + return wireless.GetWirelessIP('') + +def is_wifi_connected_now(): + if wireless == None: + return False + + wireless_connecting = wireless.CheckIfWirelessConnecting() + fast = not daemon.NeedsExternalCalls() + if wireless_connecting: + return False + else: + if not fast: + iwconfig = wireless.GetIwconfig() + else: + iwconfig = '' + if check_for_wireless(iwconfig,wireless.GetWirelessIP(''),None): + return True + else: + return False + +def check_for_wireless(iwconfig,wireless_ip,set_status): + if not wireless_ip: + return False + network = wireless.GetCurrentNetwork(iwconfig) + if not network: + return False + network = misc.to_unicode(network) + if daemon.GetSignalDisplayType() == 0: + strength = wireless.GetCurrentSignalStrength(iwconfig) + else: + strength = wireless.GetCurrentDBMStrength(iwconfig) + + if strength is None: + return False + + strength = misc.to_unicode(daemon.FormatSignalForPrinting(strength)) + ip = misc.to_unicode(wireless_ip) + + """ + print(_('dbus Connected to $A at $B (IP: $C)').replace + ('$A', network).replace + ('$B', strength).replace + ('$C', ip)) + """ + return True diff --git a/sys.py/libs/MPD/poller.py b/sys.py/libs/MPD/poller.py new file mode 100644 index 0000000..dbc372a --- /dev/null +++ b/sys.py/libs/MPD/poller.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# + +from mpd import MPDClient, MPDError, CommandError +import sys +import os + + +class PollerError(Exception): + """Fatal error in poller.""" + + +class MPDPoller(object): + _host="/tmp/mpd/socket" + #_host = "localhost" + _port="6600" + _client= None + + def __init__(self, host, port="6600"): + self._host = host + self._port = port + self._client = MPDClient(use_unicode=True) + self._client.timeout = 60*60*1000 + + def connect(self): + try: + self._client.connect(self._host, self._port) + # Catch socket errors + except IOError as err: + errno, strerror = err + raise PollerError("Could not connect to '%s': %s" % + (self._host, strerror)) + + # Catch all other possible errors + except MPDError as e: + raise PollerError("Could not connect to '%s': %s" % + (self._host, e)) + + + def disconnect(self): + # Try to tell MPD to close the connection first + try: + self._client.close() + + # If that fails, ignore it and disconnect + except (MPDError, IOError): + pass + + try: + self._client.disconnect() + + # Disconnecting failed, setup a new client object instead + # This should never happen. If it does, something is seriously broken, + # and the client object shouldn't be trusted to be re-used. + except (MPDError, IOError): + self._client = MPDClient(use_unicode=True) + self._client.timeout = 60*60*1000 + + def general(self,func,*args): + ret = None + try: + ret = func( *args ) + except CommandError: + return False + except (MPDError, IOError): + print("first error") + self.disconnect() + + try: + self.connect() + + except PollerError as e: + raise PollerError("Reconnecting failed: %s" % e) + + try: + ret = func(*args) + except (MPDError, IOError) as e: + raise PollerError("Couldn't retrieve current song: %s" % e) + + return ret + + def ping(self): + return self.general(self._client.ping) + + def poll(self): + song = self.general( self._client.status ) + """ + while playing: + {u'songid': u'4', u'playlistlength': u'4', u'playlist': u'7', u'repeat': u'0', u'consume': u'0', u'mixrampdb': u'0.000000', u'random': u'0', u'state': u'play', u'elapsed': u'148.758', u'volume': u'100', u'single': u'0', u'time': u'149:436', u'duration': u'435.670', u'song': u'3', u'audio': u'44100:24:2', u'bitrate': u'192'} + + """ + +# print(song) + return song + + def stop(self): + self.general(self._client.stop) + + def addfile(self,url): + self.general(self._client.add, url) + + def delete(self,posid): + self.general(self._client.delete,posid) + + def play(self,posid): + + song = self.poll() + + if "song" in song: + if int(song["song"]) != posid: + self.general(self._client.play,posid) + else: + if "state" in song: + if song["state"] == "play": + self.general(self._client.pause) + elif song["state"] == "pause": + self.general(self._client.pause) + elif song["state"] == "stop": + self.general(self._client.play,posid) + else: + self.general(self._client.play,posid) + + self.general(self._client.setvol,100) + + return posid + + def playlist(self): + lst = self.general(self._client.playlistinfo) + return lst + for i in lst: + if "title" in i: + print( i["title"] ) + elif "file" in i: + print( os.path.basename( i["file"] ) ) + + def listfiles(self,path): + files = self.general(self._client.lsinfo,path) + return files + for i in sorted(files): + if "directory" in i: + print( "D %s" % i["directory"] ) + elif "file" in i: + print(i["file"]) + + def rootfiles(self): + files = self.general(self._client.lsinfo, "/") + return files + for i in sorted(files): + if "directory" in i: + print( "D %s" % i["directory"] ) + elif "file" in i: + print(i["file"]) + +def main(): + from time import sleep + + poller = MPDPoller() + poller.connect() + + while True: + print("poll:") + print( poller.poll() ) + + """ + print("playlist:") + print(poller.playlist()) + print("rootfiles:") + poller.rootfiles() + """ + sleep(120) + + +if __name__ == "__main__": + import sys + + try: + main() + + except PollerError as e: + print("Fatal poller error: %s" % e) + sys.exit(1) + + except Exception as e: + print("Unexpected exception: %s" % e) + sys.exit(1) + + except: + sys.exit(0) + + diff --git a/sys.py/libs/easing.py b/sys.py/libs/easing.py new file mode 100644 index 0000000..28d4ae2 --- /dev/null +++ b/sys.py/libs/easing.py @@ -0,0 +1,7 @@ + +import math + + +def SineIn(t, b, c, d): + return -c * math.cos(t/d * (math.pi/2)) + c + b + diff --git a/sys.py/libs/roundrects/__init__.py b/sys.py/libs/roundrects/__init__.py new file mode 100644 index 0000000..73cdbbd --- /dev/null +++ b/sys.py/libs/roundrects/__init__.py @@ -0,0 +1 @@ +from .roundrects import aa_round_rect, round_rect diff --git a/sys.py/libs/roundrects/roundrects.py b/sys.py/libs/roundrects/roundrects.py new file mode 100644 index 0000000..aa01c20 --- /dev/null +++ b/sys.py/libs/roundrects/roundrects.py @@ -0,0 +1,60 @@ +""" +Rounded rectangles in both non-antialiased and antialiased varieties. +""" + +import pygame as pg + +from pygame import gfxdraw + + +def round_rect(surface, rect, color, rad=20, border=0, inside=(0,0,0,0)): + """ + Draw a rect with rounded corners to surface. Argument rad can be specified + to adjust curvature of edges (given in pixels). An optional border + width can also be supplied; if not provided the rect will be filled. + Both the color and optional interior color (the inside argument) support + alpha. + """ + rect = pg.Rect(rect) + zeroed_rect = rect.copy() + zeroed_rect.topleft = 0,0 + image = pg.Surface(rect.size).convert_alpha() + image.fill((0,0,0,0)) + _render_region(image, zeroed_rect, color, rad) + if border: + zeroed_rect.inflate_ip(-2*border, -2*border) + _render_region(image, zeroed_rect, inside, rad) + surface.blit(image, rect) + + +def _render_region(image, rect, color, rad): + """Helper function for round_rect.""" + corners = rect.inflate(-2*rad, -2*rad) + for attribute in ("topleft", "topright", "bottomleft", "bottomright"): + pg.draw.circle(image, color, getattr(corners,attribute), rad) + image.fill(color, rect.inflate(-2*rad,0)) + image.fill(color, rect.inflate(0,-2*rad)) + + +def aa_round_rect(surface, rect, color, rad=20, border=0, inside=(0,0,0)): + """ + Draw an antialiased rounded rect on the target surface. Alpha is not + supported in this implementation but other than that usage is identical to + round_rect. + """ + rect = pg.Rect(rect) + _aa_render_region(surface, rect, color, rad) + if border: + rect.inflate_ip(-2*border, -2*border) + _aa_render_region(surface, rect, inside, rad) + + +def _aa_render_region(image, rect, color, rad): + """Helper function for aa_round_rect.""" + corners = rect.inflate(-2*rad-1, -2*rad-1) + for attribute in ("topleft", "topright", "bottomleft", "bottomright"): + x, y = getattr(corners, attribute) + gfxdraw.aacircle(image, x, y, rad, color) + gfxdraw.filled_circle(image, x, y, rad, color) + image.fill(color, rect.inflate(-2*rad,0)) + image.fill(color, rect.inflate(0,-2*rad)) diff --git a/sys.py/proc/cpuinfo b/sys.py/proc/cpuinfo new file mode 100644 index 0000000..fc295a8 --- /dev/null +++ b/sys.py/proc/cpuinfo @@ -0,0 +1,43 @@ +processor : 0 +model name : ARMv7 Processor rev 5 (v7l) +BogoMIPS : 48.00 +Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0xc07 +CPU revision : 5 + +processor : 1 +model name : ARMv7 Processor rev 5 (v7l) +BogoMIPS : 48.00 +Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0xc07 +CPU revision : 5 + +processor : 2 +model name : ARMv7 Processor rev 5 (v7l) +BogoMIPS : 48.00 +Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0xc07 +CPU revision : 5 + +processor : 3 +model name : ARMv7 Processor rev 5 (v7l) +BogoMIPS : 48.00 +Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0xc07 +CPU revision : 5 + +Hardware : Generic DT based system +Revision : 0000 +Serial : 165541530903875e diff --git a/sys.py/proc/driver/backlight b/sys.py/proc/driver/backlight new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/sys.py/proc/driver/backlight @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/sys.py/run.py b/sys.py/run.py new file mode 100644 index 0000000..bbb9918 --- /dev/null +++ b/sys.py/run.py @@ -0,0 +1,324 @@ +# -*- coding: utf-8 -*- + +import dbus +import dbus.service +import sys +from wicd import misc +##misc.to_bool +##misc.misc.noneToString +##misc.to_unicode +##misc.Noneify +from wicd.translations import _ +from wicd import wpath +from wicd import dbusmanager +import time +import gobject + + +import pygame +from sys import exit +import os + +from beeprint import pp +######## +if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): + import dbus.glib +else: + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + + +#local UI import +from UI.constants import Width,Height,bg_color,icon_width,icon_height,DT,GMEVT,RUNEVT,RUNSYS +from UI.util_funcs import ReplaceSuffix,FileExists, ReadTheFileContent,midRect,color_surface,SwapAndShow,GetExePath,X_center_mouse +from UI.page import PageStack,PageSelector,Page +from UI.label import Label +from UI.icon_item import IconItem +#from UI.fonts import fonts +from UI.title_bar import TitleBar +from UI.foot_bar import FootBar +from UI.main_screen import MainScreen +from UI.above_all_patch import SoundPatch +from UI.icon_pool import MyIconPool + +from libs.DBUS import setup_dbus + +import config + +if not pygame.display.get_init(): + pygame.display.init() +if not pygame.font.get_init(): + pygame.font.init() + +gobject_main_loop = None + +sound_patch = None + +myscriptname = os.path.basename(os.path.realpath(__file__)) + +everytime_keydown = time.time() + +last_brt = -1 + +def gobject_loop(): + """ + here to receive dbus signal + """ + try: + gobject_main_loop.run() + except KeyboardInterrupt: + gobject_main_loop.quit() + exit(-1) + + +def RestoreLastBackLightBrightness(main_screen): + global last_brt + + if last_brt == -1: + return + + try: + f = open(config.BackLight,"r+") + except IOError: + print( "RestoreLastBackLightBrightness open %s failed, try to adjust brightness in Settings" % config.BackLight) + pass + else: + with f: + content = f.readlines() + content = [x.strip() for x in content] + brt=int(content[0]) + if brt < last_brt: + f.seek(0) + f.write(str( last_brt )) + f.truncate() + f.close() + last_brt = -1 + main_screen._TitleBar._InLowBackLight = -1 + else: + + f.close() + return + +def InspectionTeam(main_screen): + global everytime_keydown,last_brt + + cur_time = time.time() + + if cur_time - everytime_keydown > 40: + print("timeout, dim screen %d" % int(cur_time - everytime_keydown)) + + try: + f = open(config.BackLight,"r+") + except IOError: + pass + else: + with f: + content = f.readlines() + content = [x.strip() for x in content] + brt=int(content[0]) + if brt > 1: + last_brt = brt ## remember brt for restore + brt = 1 + f.seek(0) + f.write(str(brt)) + f.truncate() + f.close() + + main_screen._TitleBar._InLowBackLight = 0 + + everytime_keydown = cur_time + + return True + +def event_process(event,main_screen): + global sound_patch + global everytime_keydown + if event != None: + + pygame.event.clear() + + if event.type == pygame.ACTIVEEVENT: + print(" ACTIVEEVENT !") + return + if event.type == pygame.QUIT: + exit() + if event.type == GMEVT: + main_screen.Draw() + main_screen.SwapAndShow() + pygame.event.clear(GMEVT) + return + if event.type == RUNEVT: + + if config.DontLeave==True: + os.chdir(GetExePath()) + os.system( "/bin/sh -c "+event.message) + else: + on_exit_cb = getattr(main_screen,"OnExitCb",None) + if on_exit_cb != None: + if callable( on_exit_cb ): + main_screen.OnExitCb(event) + + pygame.quit() + gobject_main_loop.quit() + os.chdir( GetExePath()) + exec_app_cmd = event.message + exec_app_cmd += "; sync & cd "+GetExePath()+"; exec python "+myscriptname + print(exec_app_cmd) + os.execlp("/bin/sh","/bin/sh","-c", exec_app_cmd) + os.chdir( GetExePath()) + os.exelp("python","python"," "+myscriptname) + sys.exit(-1) + return + + if event.type == RUNSYS: + if config.DontLeave==True: + os.chdir(GetExePath()) + os.system( "/bin/sh -c "+event.message) + else: + pygame.quit() + gobject_main_loop.quit() + os.chdir( GetExePath()) + exec_app_cmd = event.message + exec_app_cmd += "; sync & cd "+GetExePath()+"; exec python "+myscriptname + print(exec_app_cmd) + os.execlp("/bin/sh","/bin/sh","-c", exec_app_cmd) + os.chdir( GetExePath()) + os.exelp("python","python"," "+myscriptname) + return + if event.type == pygame.KEYUP: + + pygame.event.clear(pygame.KEYDOWN) + return + if event.type == pygame.KEYDOWN: + everytime_keydown = time.time() + RestoreLastBackLightBrightness(main_screen) + ########################################################### + if event.key == pygame.K_q: + on_exit_cb = getattr(main_screen,"OnExitCb",None) + if on_exit_cb != None: + if callable( on_exit_cb ): + main_screen.OnExitCb(event) + + gobject_main_loop.quit() + exit() + + if event.key == pygame.K_KP_PLUS: + + main_screen.Draw() + sound_patch.VolumeUp() + sound_patch.Draw() + + main_screen.SwapAndShow() + #pygame.time.delay(200) + #main_screen.Draw() + #main_screen.SwapAndShow() + + if event.key == pygame.K_KP_MINUS: + main_screen.Draw() + + sound_patch.VolumeDown() + sound_patch.Draw() + + main_screen.SwapAndShow() + #pygame.time.delay(200) + #main_screen.Draw() + #main_screen.SwapAndShow() + + + ########################################################### + if event.key == pygame.K_ESCAPE: + pygame.event.clear() + + key_down_cb = getattr(main_screen,"KeyDown",None) + if key_down_cb != None: + if callable( key_down_cb ): + main_screen.KeyDown(event) + + return + +def gobject_pygame_event_poll_timer(main_screen): + + event = pygame.event.poll() + + event_process(event,main_screen) + + InspectionTeam(main_screen) + + return True + +def gobject_pygame_event_timer(main_screen): + global sound_patch + + for event in pygame.event.get(): + event_process(event,main_screen) + + return True + +def big_loop(): + global sound_patch + + title_bar = TitleBar() + title_bar.Init(screen) + foot_bar = FootBar() + foot_bar.Init(screen) + + main_screen = MainScreen() + main_screen._HWND = screen + main_screen._TitleBar = title_bar + main_screen._FootBar = foot_bar + main_screen.Init() + main_screen.ReadTheDirIntoPages("../Menu",0,None) + main_screen.FartherPages() + + + sound_patch = SoundPatch() + sound_patch._Parent = main_screen + sound_patch.Init() + #pp(main_screen._Pages[0],True,6) + + screen.fill(bg_color) + main_screen.Draw() + main_screen.SwapAndShow() + + #gobject.timeout_add(DT,gobject_pygame_event_timer,main_screen) + gobject.timeout_add(DT,gobject_pygame_event_poll_timer,main_screen) + gobject.timeout_add(3000,title_bar.GObjectRoundRobin) + + + gobject_loop() + + +###MAIN()### +if __name__ == '__main__': + + os.environ['SDL_VIDEO_CENTERED'] = '1' + X_center_mouse() + + os.chdir( os.path.dirname(os.path.realpath(__file__)) ) + + SCREEN_SIZE = (Width,Height) + screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32) + + pygame.event.set_allowed(None) + pygame.event.set_allowed([pygame.KEYDOWN,pygame.KEYUP,GMEVT,RUNEVT,RUNSYS]) + + pygame.key.set_repeat(DT+234, DT+123) + + + MyIconPool.Init() + + setup_dbus() + + gobject.threads_init() + + gobject_main_loop = gobject.MainLoop() + +# if pygame.display.get_active() == True: +# print("I am actived") + + if pygame.image.get_extended() == False: + print("This pygame does not support PNG") + exit() + + big_loop() + diff --git a/sys.py/sys/class/power_supply/axp20x-battery/uevent b/sys.py/sys/class/power_supply/axp20x-battery/uevent new file mode 100644 index 0000000..f2a820f --- /dev/null +++ b/sys.py/sys/class/power_supply/axp20x-battery/uevent @@ -0,0 +1,12 @@ +POWER_SUPPLY_NAME=axp20x-battery +POWER_SUPPLY_PRESENT=1 +POWER_SUPPLY_ONLINE=1 +POWER_SUPPLY_STATUS=Discharging +POWER_SUPPLY_VOLTAGE_NOW=3967000 +POWER_SUPPLY_CURRENT_NOW=258000 +POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=1200000 +POWER_SUPPLY_CONSTANT_CHARGE_CURRENT_MAX=1200000 +POWER_SUPPLY_HEALTH=Good +POWER_SUPPLY_VOLTAGE_MAX_DESIGN=4200000 +POWER_SUPPLY_VOLTAGE_MIN_DESIGN=2900000 +POWER_SUPPLY_CAPACITY=30