diff --git a/g13gui/g13gui/applet/__init__.py b/g13gui/g13gui/applet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/g13gui/g13gui/applet/applet.py b/g13gui/g13gui/applet/applet.py new file mode 100644 index 0000000..dd534a4 --- /dev/null +++ b/g13gui/g13gui/applet/applet.py @@ -0,0 +1,160 @@ +import gi +import dbus +import dbus.service +import dbus.mainloop.glib +import time + +from dbus.mainloop.glib import DBusGMainLoop +from dbus.exceptions import DBusException +from dbus.types import ByteArray + +from g13gui.applet.loopbackdisplaydevice import LoopbackDisplayDevice +from g13gui.bitwidgets.display import Display +from g13gui.bitwidgets.screen import Screen + +gi.require_version('GLib', '2.0') +from gi.repository import GLib + + +class Buttons(object): + L1 = 1 + L2 = 2 + L3 = 3 + L4 = 4 + + +class Applet(dbus.service.Object): + BUS_INTERFACE = 'com.theonelab.g13.Applet' + BUS_PATH = '/com/theonelab/g13/Applet' + + def __init__(self, name): + dbus.service.Object.__init__(self, dbus.SessionBus(), + Applet.BUS_PATH) + + self._name = name + self._dd = LoopbackDisplayDevice() + self._d = Display(self._dd) + self._s = Screen(self._d) + self._s.hide() + + self._registered = False + self._manager = None + + def register(self): + try: + self._manager = self._bus.get_object( + 'com.theonelab.g13.AppletManager', + '/com/theonelab/g13/AppletManager') + except DBusException as err: + self._manager = None + return True + + self._manager.Register(self._name) + self._registered = True + GLib.timeout_add_seconds(1, self._ping) + + return False + + def _ping(self): + if self._manager: + try: + self._manager.Ping() + except DBusException as err: + print('Lost connection with AppletManager: %s' % err) + self._registered = False + GLib.timeout_add_seconds(1, self.register) + return False + + return True + + def run(self): + self._bus = dbus.SessionBus() + + GLib.timeout_add_seconds(1, self.register) + + loop = GLib.MainLoop() + loop.run() + + @property + def name(self): + return self._name + + @property + def displayDevice(self): + return self._dd + + @property + def display(self): + return self._d + + @property + def screen(self): + return self._s + + def onKeyPressed(self, timestamp, key): + pass + + def onKeyReleased(self, timestamp, key): + pass + + def onShown(self, timestamp): + pass + + def onHidden(self): + pass + + def maybePresentScreen(self): + if self.screen.visible and self._manager: + self.screen.nextFrame() + frame = self.displayDevice.frame + frame = ByteArray(frame) + self._manager.Present(frame, byte_arrays=True) + + @dbus.service.method(BUS_INTERFACE, + in_signature='d', out_signature='ay', + byte_arrays=True) + def Present(self, timestamp): + self.screen.show() + self.onShown(timestamp) + self.screen.nextFrame() + return ByteArray(self.displayDevice.frame) + + @dbus.service.method(BUS_INTERFACE) + def Unpresent(self): + self.screen.hide() + self.onHidden() + + def _setButtonPressed(self, state, button): + buttonIdx = button - 1 + button = self._s.buttonBar.button(buttonIdx) + if button: + button.pressed = state + + def onUpdateScreen(self): + pass + + @dbus.service.method(BUS_INTERFACE, + in_signature='di', out_signature='ay', + byte_arrays=True) + def KeyPressed(self, timestamp, key): + self.onKeyPressed(timestamp, key) + self._setButtonPressed(True, key) + self.onUpdateScreen() + self.screen.nextFrame() + return ByteArray(self.displayDevice.frame) + + @dbus.service.method(BUS_INTERFACE, + in_signature='di', out_signature='ay', + byte_arrays=True) + def KeyReleased(self, timestamp, key): + self.onKeyPressed(timestamp, key) + self._setButtonPressed(False, key) + self.onUpdateScreen() + self.screen.nextFrame() + return ByteArray(self.displayDevice.frame) + + +def RunApplet(cls, *args, **kwargs): + DBusGMainLoop(set_as_default=True) + applet = cls(*args, **kwargs) + applet.run() diff --git a/g13gui/g13gui/applet/loopbackdisplaydevice.py b/g13gui/g13gui/applet/loopbackdisplaydevice.py new file mode 100644 index 0000000..edb45f3 --- /dev/null +++ b/g13gui/g13gui/applet/loopbackdisplaydevice.py @@ -0,0 +1,24 @@ +from builtins import property + +from g13gui.bitwidgets.displaydevice import DisplayDevice +from g13gui.g13.displaydevice import G13DisplayDevice + + +class LoopbackDisplayDevice(G13DisplayDevice): + """A loopback display device for the G13 manager. + + This one differs from the built-in G13DisplayDevice by preventing a direct + write to the G13Manager's setLCDbuffer. This is specifically designed for + the Applet use case, where the methods return LCD frames, rather than write + directly. + """ + + def __init__(self): + pass + + @property + def frame(self): + return self._frame + + def _pushFrame(self, lpbm): + self._frame = lpbm diff --git a/g13gui/g13gui/applet/manager.py b/g13gui/g13gui/applet/manager.py new file mode 100644 index 0000000..75eec1a --- /dev/null +++ b/g13gui/g13gui/applet/manager.py @@ -0,0 +1,115 @@ +import gi +import dbus +import dbus.service +import dbus.mainloop.glib +import time + +from builtins import property + +from g13gui.observer.subject import Subject +from g13gui.observer.subject import ChangeType +from g13gui.applet.switcher import Switcher +from g13gui.g13.common import G13Keys + +gi.require_version('GLib', '2.0') +from gi.repository import GLib + + +class AppletManager(dbus.service.Object, Subject): + INTERFACE_NAME = 'com.theonelab.g13.AppletManager' + BUS_NAME = 'com.theonelab.g13.AppletManager' + BUS_PATH = '/com/theonelab/g13/AppletManager' + + def __init__(self, manager): + self._bus = dbus.SessionBus() + self._busName = dbus.service.BusName(AppletManager.BUS_NAME, self._bus) + dbus.service.Object.__init__(self, self._bus, + AppletManager.BUS_PATH) + Subject.__init__(self) + + self._manager = manager + + # [name] -> (sender, proxy) + self._applets = {} + + self._switcher = Switcher(self) + self._activeApplet = self._switcher + + self._applets['Switcher'] = (self._switcher, self._switcher) + self.addChange(ChangeType.ADD, 'applet', 'Switcher') + self.notifyChanged() + + @property + def activeApplet(self): + return self._activeApplet + + @activeApplet.setter + def activeApplet(self, appletName): + (name, appletProxy) = self._applets[appletName] + self._activeApplet.Unpresent() + self.setProperty('activeApplet', appletProxy) + self.onPresent() + + def raiseSwitcher(self): + self._activeApplet = self._switcher + self.onPresent() + + @property + def appletNames(self): + return self._applets.keys() + + def _updateLCD(self, frame): + self._manager.setLCDBuffer(frame) + + def onPresent(self): + frame = self._activeApplet.Present(time.time(), byte_arrays=True) + frame = bytes(frame) + self._updateLCD(frame) + + def onKeyPressed(self, key): + # Swap to the switcher + if key == G13Keys.BD: + self.activeApplet = 'Switcher' + return + + frame = self._activeApplet.KeyPressed(time.time(), key.value['bit']) + self._updateLCD(frame) + + def onKeyReleased(self, key): + frame = self._activeApplet.KeyReleased(time.time(), key.value['bit']) + self._updateLCD(frame) + + def _registerApplet(self, name, sender): + proxy = self._bus.get_object(sender, '/com/theonelab/g13/Applet') + self._applets[name] = (sender, proxy) + self.addChange(ChangeType.ADD, 'applet', name) + self.notifyChanged() + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='s', sender_keyword='sender') + def Register(self, name, sender): + if sender is None: + print('Attempt to register None as sender applet!') + return False + + print('Registered applet %s as %s' % (name, sender)) + GLib.idle_add(self._registerApplet, str(name), sender) + + def _presentScreen(self, screen, sender): + self._updateLCD(screen) + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='ay', sender_keyword='sender', + byte_arrays=True) + def Present(self, screen, sender): + # if self._activeApplet.bus_name != sender: + # return + GLib.idle_add(self._presentScreen, screen, sender) + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + out_signature='b', + sender_keyword='sender') + def Ping(self, sender): + if sender not in [s[0] for s in self._applets]: + return False + return True diff --git a/g13gui/g13gui/applet/switcher.py b/g13gui/g13gui/applet/switcher.py new file mode 100644 index 0000000..3882557 --- /dev/null +++ b/g13gui/g13gui/applet/switcher.py @@ -0,0 +1,101 @@ +import gi +import time +import threading + +from builtins import property + +from g13gui.observer.observer import Observer +from g13gui.observer.subject import ChangeType +from g13gui.applet.applet import Buttons +from g13gui.applet.loopbackdisplaydevice import LoopbackDisplayDevice +from g13gui.bitwidgets.display import Display +from g13gui.bitwidgets.screen import Screen +from g13gui.bitwidgets.label import Label +from g13gui.bitwidgets.button import Button +from g13gui.bitwidgets.button import Glyphs +from g13gui.bitwidgets.listview import ListView + +gi.require_version('GLib', '2.0') +from gi.repository import GLib + + +class Switcher(Observer): + def __init__(self, appletManager): + Observer.__init__(self) + + self._appletManager = appletManager + self._applets = [] + + self._appletManager.registerObserver(self, {'activeApplet', 'applet'}) + self.changeTrigger(self.onNewApplet, + changeType=ChangeType.ADD, + keys={'applet'}) + + self._initWidgets() + + @property + def bus_name(self): + return self + + def onNewApplet(self, subject, changeType, key, data): + self._applets = sorted(self._appletManager.appletNames) + self._lv.model = self._applets + self._lv.update() + + self._s.nextFrame() + frame = self._dd.frame + self._appletManager.Present(frame, self) + + def _initWidgets(self): + self._dd = LoopbackDisplayDevice() + self._d = Display(self._dd) + self._s = Screen(self._d) + + self._lv = ListView(self._applets) + self._lv.showAll() + self._s.addChild(self._lv) + + button = Button(Glyphs.DOWN_ARROW) + self._s.buttonBar.setButton(1, button) + button = Button(Glyphs.UP_ARROW) + self._s.buttonBar.setButton(2, button) + button = Button(Glyphs.CHECKMARK) + self._s.buttonBar.setButton(3, button) + self._s.buttonBar.showAll() + + def _setButtonPressed(self, state, button): + buttonIdx = button - 1 + button = self._s.buttonBar.button(buttonIdx) + if button: + button.pressed = state + + def Present(self, timestamp, **kwargs): + self._s.show() + self._lv.update() + self._s.nextFrame() + frame = self._dd.frame + return frame + + def Unpresent(self): + self._s.hide() + + def KeyPressed(self, timestamp, key): + self._setButtonPressed(True, key) + return self.Present(timestamp) + + def _setActiveApplet(self): + selectedName = self._lv.markedItem() + self._appletManager.activeApplet = selectedName + + def KeyReleased(self, timestamp, key): + self._setButtonPressed(False, key) + + if key == Buttons.L2: # down + self._lv.nextSelection() + elif key == Buttons.L3: # up + self._lv.prevSelection() + elif key == Buttons.L4: # select + self._lv.markSelection() + GLib.idle_add(self._setActiveApplet) + + return self.Present(timestamp) diff --git a/g13gui/g13gui/applets/__init__.py b/g13gui/g13gui/applets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/g13gui/g13gui/applets/clock.py b/g13gui/g13gui/applets/clock.py new file mode 100644 index 0000000..0521166 --- /dev/null +++ b/g13gui/g13gui/applets/clock.py @@ -0,0 +1,41 @@ +import gi +import time + +from g13gui.applet.applet import Applet +from g13gui.applet.applet import RunApplet +from g13gui.bitwidgets.label import Label +from g13gui.bitwidgets.fonts import Fonts + +gi.require_version('GLib', '2.0') +from gi.repository import GLib + + +class ClockApplet(Applet): + NAME = 'Clock' + + def __init__(self): + Applet.__init__(self, ClockApplet.NAME) + + self._timeLabel = Label(43, 8, '18:54:00', font=Fonts.LARGE) + self._timeLabel.showAll() + self.screen.addChild(self._timeLabel) + + self._updateTimeLabel() + + def _updateTimeLabel(self): + (tm_year, tm_month, tm_mday, tm_hour, + tm_min, tm_sec, tm_wday, tm_yday, tm_isdst) = time.localtime() + self._timeLabel.text = '%d:%0.2d:%0.2d' % (tm_hour, tm_min, tm_sec) + + def _pushTime(self): + self._updateTimeLabel() + self.maybePresentScreen() + return self.screen.visible + + def onShown(self, timestamp): + self._updateTimeLabel() + GLib.timeout_add_seconds(1, self._pushTime) + + +if __name__ == '__main__': + RunApplet(ClockApplet)