roundabout,
created on Tuesday, 28 October 2025, 20:22:58 (1761682978),
received on Tuesday, 28 October 2025, 20:31:10 (1761683470)
Author identity: Vlad <vlad.muntoiu@gmail.com>
382f5be16f55a9e39869755255cf4d3b08cf1bbe
ascii.xkb
@@ -1,23 +0,0 @@
xkb_keymap {
xkb_keycodes "(unnamed)" {
minimum = 8;
maximum = 255;
<BKSP> = 22;
<RTRN> = 36;
};
xkb_types "(unnamed)" {
type "ONE_LEVEL" {
modifiers= none;
level_name[Level1]= "Any";
};
};
xkb_compatibility "(unnamed)" {
};
xkb_symbols "(unnamed)" {
key <BKSP> { [ BackSpace ] };
key <RTRN> { [ Return ] };
};
};
layouts/num.yaml
@@ -0,0 +1,73 @@
info: "
Kineboard: Layout with the numerals and useful operators.
Copyright 2025, roundabout-host.com <vlad@roundabout-host.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public Licence as published by
the Free Software Foundation, either version 3 of the Licence, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public Licence for more details.
You should have received a copy of the GNU General Public Licence
along with this program. If not, see <https://www.gnu.org/licenses/>.
"
name: "Numbers"
symbol: "12+"
description: "Layout with the numerals and useful programming operators"
keys:
-
-
- central: "0"
top_left: "~"
top_right: "`"
bottom_right: "^"
bottom_left: "&"
- central: "2"
top_left: "{"
top_right: "}"
bottom_right: "]"
bottom_left: "["
- central: "4"
top_left: "+"
top_right: "-"
bottom_right: "*"
bottom_left: "/"
- central: "6"
top_left: "<"
top_right: ">"
bottom_right: ")"
bottom_left: "("
- central: "8"
top_left: "\""
top_right: "\\"
bottom_right: "|"
bottom_left: "="
-
- central: "1"
top_left: "!"
top_right: "%"
bottom_right: "#"
bottom_left: "@"
- central: "3"
top_left: "."
top_right: ","
bottom_right: "_"
bottom_left: "$"
- central: "5"
top_left: "="
top_right: "?"
bottom_right: "=="
bottom_left: "!="
- central: "7"
top_left: ";"
top_right: ":"
bottom_right: "<<"
bottom_left: ">>"
- central: "9"
top_left: "/*"
top_right: "&&"
bottom_right: "||"
bottom_left: "*/"
main.py
@@ -234,6 +234,8 @@ class Kineboard(Gtk.Application):
application_id="com.roundabout_host.roundabout.Kineboard",
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE
)
self.revealer = None
self.switcher = None
self.character_index = {}
self.keymap_fd = None
self.vk = None
@@ -267,6 +269,9 @@ class Kineboard(Gtk.Application):
None
)
def toggle_sidebar(self, button):
self.revealer.set_reveal_child(not self.revealer.get_reveal_child())
def do_startup(self):
Gtk.Application.do_startup(self)
self.display = Gdk.Display.get_default()
@@ -277,12 +282,15 @@ 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)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.window.set_child(self.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())
self.all_characters = ""
self.all_characters = " "
self.switcher = Gtk.StackSidebar(stack=self.stack)
self.revealer = Gtk.Revealer(child=self.switcher, transition_type=Gtk.RevealerTransitionType.SLIDE_RIGHT)
self.box.prepend(self.revealer)
for layout in layouts:
stack = Gtk.Stack()
@@ -292,6 +300,12 @@ class Kineboard(Gtk.Application):
controls = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8, margin_top=0,
margin_bottom=8, margin_start=8, margin_end=8)
layout_button = Gtk.Button(child=Gtk.Image.new_from_icon_name("edit-symbolic"))
layout_button.set_size_request(48, -1)
layout_button.update_property([Gtk.AccessibleProperty.LABEL], ["Switch layout"])
layout_button.connect("clicked", self.toggle_sidebar)
controls.append(layout_button)
if len(data["keys"]) >= 2:
shift_button = Gtk.ToggleButton(
child=Gtk.Image.new_from_icon_name("go-up-symbolic")
@@ -333,7 +347,7 @@ class Kineboard(Gtk.Application):
outer_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
outer_box.append(stack)
outer_box.append(controls)
self.stack.add_child(outer_box)
self.stack.add_titled(outer_box, data["symbol"], data["name"])
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (ctypes.py_object,)
@@ -358,7 +372,6 @@ class Kineboard(Gtk.Application):
self.character_index = {}
for i, character in enumerate(self.all_characters):
self.character_index[character] = i
print(self.keymap_text)
self.vk = self.vkm.create_virtual_keyboard(self.seat)
self.keymap_fd = os.memfd_create("keymap")
@@ -397,10 +410,11 @@ class Kineboard(Gtk.Application):
clock = int(time.monotonic() * 1000)
if self.vk:
self.vk.key(clock, key, 1)
self.vk.key(clock + 1, key, 0)
self.vk.key(clock, key, 0)
def typed(self, button, character):
self.send_keysym(0xFF8 + self.character_index[character])
def typed(self, button, characters):
for char in characters:
self.send_keysym(0xFF8 + self.character_index[char])
def do_activate(self):
Gtk.Application.do_activate(self)