From 4e56eeeda5bbfa2a5b584935c17b55718251ca12 Mon Sep 17 00:00:00 2001 From: mar77i Date: Fri, 19 Sep 2025 10:40:17 +0200 Subject: [PATCH] store settings in a keyboard config file --- keyboard/config.py | 31 +++++++++++++++++++++++++ keyboard/keyboard.py | 16 +++++++------ keyboard/layout.py | 55 +++++++++++++++++++++++++++++++++++++++++--- keyboard/menu.py | 38 +++++++++++++++++++++--------- 4 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 keyboard/config.py diff --git a/keyboard/config.py b/keyboard/config.py new file mode 100644 index 0000000..60cd65d --- /dev/null +++ b/keyboard/config.py @@ -0,0 +1,31 @@ +import json +import os +import sys +from traceback import print_exc +from pathlib import Path + + +class Config: + def __init__(self, base): + self.path = Path( + os.environ.get( + "XDG_CONFIG_HOME", f"{os.path.expanduser('~')}{os.pathsep}.config" + ) + ) / "zenbook_gui" / f"{base}.json" + self.loaded = None + + def load(self): + try: + with self.path.open("rt") as fh: + self.loaded = json.load(fh) + return self.loaded + except Exception: + print_exc(file=sys.stderr) + return None + + def save(self, data): + if data == self.loaded: + return + self.path.parent.mkdir(0o755, parents=True, exist_ok=True) + with self.path.open("wt") as fh: + json.dump(data, fh) diff --git a/keyboard/keyboard.py b/keyboard/keyboard.py index ef4acdd..fe1c8ce 100644 --- a/keyboard/keyboard.py +++ b/keyboard/keyboard.py @@ -34,7 +34,7 @@ class Root(BaseRoot): self.led_mask = self.display.get_keyboard_control().led_mask FPSWidget(self) size = self.surf.get_size() - self.settings_modal = MenuModal( + self.menu_modal = MenuModal( self, pygame.Rect((size[0] // 4, size[1] // 4), (size[0] // 2, size[1] // 2)), ) @@ -48,15 +48,15 @@ class Root(BaseRoot): half_height = size[1] // 2 for row in range(2): widget_top = half_height * row - if row == self.settings_modal.layout_modal.keyboard_row: + if row == self.menu_modal.layout_modal.keyboard_row: get_keyboard_keys( self, - pygame.Rect((widget_top, 0), (size[0], half_height)), + pygame.Rect((0, widget_top), (size[0], half_height)), self.key_cb, KEYBOARD, ) continue - widget_row = self.settings_modal.layout_modal.widget_row + widget_row = self.menu_modal.layout_modal.widget_row widget_width = size[0] // (3 if len(widget_row) == 3 else 2) x = 0 if len(widget_row) > 0 else size[0] // 4 for keyset in widget_row: @@ -71,7 +71,8 @@ class Root(BaseRoot): def key_cb(self, key, release=False): if key == "Settings": - self.settings_modal.activate() + if not release: + self.menu_modal.activate() if isinstance(key, (Key, KeyCode)): if release: self.keyboard.release(key) @@ -90,7 +91,7 @@ class Root(BaseRoot): if button in self.pushed: self.mouse.release(button) self.pushed.remove(button) - elif n is not None: + elif n > 1: self.mouse.click(button, n) else: self.mouse.press(button) @@ -117,7 +118,8 @@ class Root(BaseRoot): finally: for key in self.pushed: (self.mouse if isinstance(key, Button) else self.keyboard).release(key) + self.menu_modal.save_config() @property def muted(self): - return self.settings_modal.muted + return self.menu_modal.muted diff --git a/keyboard/layout.py b/keyboard/layout.py index 0035a6f..92936a1 100644 --- a/keyboard/layout.py +++ b/keyboard/layout.py @@ -152,6 +152,11 @@ class LayoutWidget(Child): self.keyboard_callback, ) + def activate(self): + self.keyboard.rect = self.keyboard_rects[self.parent.keyboard_row] + self.keyboard.dests.clear() + self.keyboard.dests.append(self.keyboard_rects[not self.parent.keyboard_row]) + def keyboard_callback(self): self.parent.keyboard_row = int( (self.keyboard.rect.top - self.rect.top) * 2 / self.rect.height @@ -173,7 +178,14 @@ class LayoutWidget(Child): class LayoutModal(QuittableModal): - def __init__(self, parent): + WIDGETS = { + "KEYPAD_KEYS": KEYPAD_KEYS, + "CURSOR_KEYS": CURSOR_KEYS, + "MousePadWidget": MousePadWidget, + "ClockWidget": ClockWidget, + } + + def __init__(self, parent, config=None): super().__init__(parent) size = self.surf.get_size() self.rect = pygame.Rect(150, 150, size[0] - 300, size[1] - 300) @@ -185,6 +197,43 @@ class LayoutModal(QuittableModal): self.deactivate, ) self.keyboard_row = 0 - self.widget_row = [CURSOR_KEYS, MousePadWidget, ClockWidget] + self.widget_row = [] half_size = (size[0] / 2, size[1] / 2) - LayoutWidget(self, pygame.Rect((half_size[0] / 2, half_size[1] / 2), half_size)) + self.layout_widget = LayoutWidget( + self, pygame.Rect((half_size[0] / 2, half_size[1] / 2), half_size) + ) + + def activate(self): + super().activate() + self.layout_widget.activate() + + def deactivate(self): + self.root.setup_widgets() + super().deactivate() + + def apply_config(self, data): + if "keyboard_row" in data: + self.keyboard_row = data["keyboard_row"] + if "widget_row" in data: + self.widget_row.clear() + self.widget_row.extend( + w for w in (self.WIDGETS.get(key) for key in data["widget_row"]) if w + ) + + @classmethod + def stringify_widgets(cls, widget_row): + result = [None] * len(widget_row) + for key, value in cls.WIDGETS.items(): + try: + index = widget_row.index(value) + except ValueError: + continue + else: + result[index] = key + return result + + def get_config(self): + return { + "keyboard_row": self.keyboard_row, + "widget_row": self.stringify_widgets(self.widget_row), + } diff --git a/keyboard/menu.py b/keyboard/menu.py index 6c3b3e3..3956486 100644 --- a/keyboard/menu.py +++ b/keyboard/menu.py @@ -1,3 +1,4 @@ +import atexit import os import sys from functools import partial @@ -8,17 +9,10 @@ import pygame from ui import QuittableModal, Rect +from .config import Config from .touchbutton import TouchButton from .layout import LayoutModal -# what settings do we need? -# - configure widgets -# - keyboard above, two widgets below -# - keyboard above, three widgets below -# - keyboard below, two widgets above -# - keyboard below, three widgets above -# - three available widgets: touchpad mouse, number keypad, cursor and navkeys - class MenuModal(QuittableModal): def __init__(self, parent, rect): @@ -26,14 +20,16 @@ class MenuModal(QuittableModal): self.rect = rect self.layout_modal = LayoutModal(parent) self.muted = False + self.config = Config("keyboard") + self.load_config() Rect(self, rect, "black", "gray") buttons_args = ( + ("Back", self.deactivate), ("Mute", None, self.muted), ("Layout...", self.layout_modal.activate), ("Fix Xkb layout", self.fix_xkb_layout), - ("Restart", self.restart), + ("Restart", self.prepare_restart), ("Exit", self.root.handle_quit), - ("Back", self.deactivate), ) width = rect.width * 2 / 3 height = rect.height / len(buttons_args) @@ -47,6 +43,20 @@ class MenuModal(QuittableModal): button.callback = partial(self.mute, button) y += height + def load_config(self): + data = self.config.load() + if not data: + return + if "layout" in data: + self.layout_modal.apply_config(data["layout"]) + if "muted" in data: + self.muted = data["muted"] + + def save_config(self): + self.config.save( + {"muted": self.muted, "layout": self.layout_modal.get_config()} + ) + def mute(self, button): self.muted ^= True button.highlight = self.muted @@ -55,7 +65,13 @@ class MenuModal(QuittableModal): run(["setxkbmap", "-synch"]) self.deactivate() - def restart(self): + def prepare_restart(self): + atexit.register(self.restart) + self.root.running = False + self.deactivate() + + @staticmethod + def restart(): executable = Path(sys.executable).name os.execl( sys.executable, -- 2.51.0