by roundabout, Tuesday, 6 October 2026, 13:56:39 (1791294999), pushed by roundabout, Tuesday, 6 October 2026, 14:18:11 (1791296291)
Author identity: Vlad <vlad.muntoiu@gmail.com>
369add6696caf8ac99196773470ce8a62367e5fa
main.py
@@ -43,6 +43,7 @@ from pywayland.protocol.input_method_unstable_v2 import (
)
from pywayland.protocol.virtual_keyboard_unstable_v1 import ZwpVirtualKeyboardManagerV1
import xkbcommon
import ctypes
from cffi import FFI
@@ -213,12 +214,22 @@ class Plane(Gtk.Grid):
def make_xkb_keymap_from_unicodes(unicodes):
symbols = [
"key <SP00> { [ BackSpace ] };",
"key <SP01> { [ Return ] };",
"key <LCTL> { [ Control_L ] };",
"key <LALT> { [ Alt_L ] };",
"key <LSHF> { [ Shift_L ] };",
"key <LSPR> { [ Super_L ] };",
"key <ESCP> { [ Escape ] };",
"key <BKSP> { [ BackSpace ] };",
"key <RTRN> { [ Return ] };",
]
keycodes = [
"<SP00> = 4094;",
"<SP01> = 4095;",
"<LCTL> = 4089;",
"<LALT> = 4090;",
"<LSHF> = 4091;",
"<LSPR> = 4092;",
"<ESCP> = 4093;",
"<BKSP> = 4094;",
"<RTRN> = 4095;",
]
for i, char in enumerate(unicodes):
keycode = 0x1000 + i
@@ -231,57 +242,34 @@ def make_xkb_keymap_from_unicodes(unicodes):
+ " }; xkb_types \"kineboard\" { type \"ONE_LEVEL\" { modifiers = none; level_name[Level1] = \"Any\"; }; }; xkb_compatibility \"kineboard\" {}; };")
from gi.repository import Gio, GLib
def register_client(client_id: str):
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
proxy = Gio.DBusProxy.new_sync(
bus,
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION,
None,
"org.gnome.SessionManager",
"/org/gnome/SessionManager",
"org.gnome.SessionManager",
None
)
startup_id = GLib.getenv("DESKTOP_AUTOSTART_ID") or ""
class ModifierButton(Gtk.ToggleButton):
def __init__(self, app, mask, **kwargs):
super().__init__(**kwargs)
self.app = app
self.mask = mask
def on_registered(proxy, result, loop):
try:
proxy.call_finish(result)
except Exception as e:
print("cannot register client:", e)
loop.quit()
self.connect("toggled", self.toggle_modifier)
loop = GLib.MainLoop()
def toggle_modifier(self, button):
if button.get_active():
self.app.depressed_modifiers |= self.mask
else:
self.app.depressed_modifiers &= ~self.mask
proxy.call(
"RegisterClient",
GLib.Variant("(ss)", (client_id, startup_id)),
Gio.DBusCallFlags.NONE,
-1,
None,
lambda p, r, l: on_registered(p, r, loop),
loop
)
self.app.send_modifiers()
loop.run()
return proxy
class Kineboard(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(
self,
application_id="sm.puri.OSK0",
application_id="com.roundabout_host.roundabout.Kineboard",
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE
)
self.xkb_context = xkbcommon.xkb.Context()
self.revealer = None
self.switcher = None
self.depressed_modifiers = 0
self.character_index = {}
self.keymap_fd = None
self.vk = None
@@ -318,10 +306,12 @@ class Kineboard(Gtk.Application):
def toggle_sidebar(self, button):
self.revealer.set_reveal_child(not self.revealer.get_reveal_child())
def send_modifiers(self):
self.vk.modifiers(self.depressed_modifiers, 0, 0, 0)
def do_startup(self):
Gtk.Application.do_startup(self)
register_client("sm.puri.OSK0")
self.display = Gdk.Display.get_default()
self.window = Gtk.Window(application=self, title="Demo", focus_on_click=False, focusable=False, can_focus=False)
Gtk4LayerShell.init_for_window(self.window)
@@ -330,9 +320,9 @@ class Kineboard(Gtk.Application):
Gtk4LayerShell.set_anchor(self.window, Gtk4LayerShell.Edge.LEFT, True)
Gtk4LayerShell.set_anchor(self.window, Gtk4LayerShell.Edge.RIGHT, True)
Gtk4LayerShell.auto_exclusive_zone_enable(self.window)
Gtk4LayerShell.set_namespace(self.window, "osk")
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.window.set_child(self.box)
self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.window.set_child(self.main_box)
self.stack = Gtk.Stack()
self.box.append(self.stack)
layouts = itertools.chain.from_iterable((f for f in path.iterdir() if f.is_file()) for path in get_layout_directories())
@@ -398,6 +388,16 @@ class Kineboard(Gtk.Application):
outer_box.append(controls)
self.stack.add_titled(outer_box, data["symbol"], data["symbol"])
self.extra_controls = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8, margin_top=0,
margin_bottom=8, margin_start=8, margin_end=8)
self.extra_controls.append(ModifierButton(self, keymap.mod_get_index("Control"), label="Control"))
self.extra_controls.append(ModifierButton(self, keymap.mod_get_index("Alt"), label="Alt"))
self.extra_controls.append(ModifierButton(self, keymap.mod_get_index("Shift"), label="Shift"))
self.extra_controls.append(ModifierButton(self, keymap.mod_get_index("Super"), label="Super"))
self.main_box.append(self.extra_controls)
self.main_box.append(self.box)
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (ctypes.py_object,)
@@ -419,6 +419,8 @@ class Kineboard(Gtk.Application):
dialog.show()
self.keymap_text = make_xkb_keymap_from_unicodes(self.all_characters)
xkb_keymap = self.xkb_context.keymap_new_from_string(self.keymap_text, xkb.XKB_KEYMAP_FORMAT_TEXT_V1)
state = keymap.state_new()
self.character_index = {}
for i, character in enumerate(self.all_characters):
self.character_index[character] = i
@@ -429,12 +431,21 @@ class Kineboard(Gtk.Application):
os.lseek(self.keymap_fd, 0, os.SEEK_SET)
self.vk.keymap(1, self.keymap_fd, len(self.keymap_text.encode("utf-8")))
def activate_input(self, im):
def activate_input(self, im, context):
self.window.present()
self.context = context
if self.context:
self.context.dispatcher["commit_state"] = self.commit_state
def commit_state(self, context, serial):
self.serial = serial
def deactivate_input(self, im):
def deactivate_input(self, im, context):
self.window.set_visible(False)
self.serial = 0
if self.context:
self.context.destroy()
self.context = None
self.serial = 0
def on_global(self, registry, name, interface, version):
if interface == "zwp_input_method_manager_v2":
@@ -443,6 +454,7 @@ class Kineboard(Gtk.Application):
self.seat = registry.bind(name, WlSeat, version)
elif interface == "zwp_virtual_keyboard_manager_v1":
self.vkm = registry.bind(name, ZwpVirtualKeyboardManagerV1, version)
print("VKM found!!")
def on_global_remove(self, registry, name):
pass