Skip to content

Commit 2a00273

Browse files
authored
#242 attempt to enhance used xlib not shipping xkb module with xcffib (#249)
1 parent 130a1b4 commit 2a00273

File tree

3 files changed

+137
-31
lines changed

3 files changed

+137
-31
lines changed

install.sh

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,71 +22,71 @@ LOGS_INSTALL_LOG_FILE_PATH="$LOGS_DIR_PATH/$LOGS_INSTALL_LOG_FILE_NAME"
2222
# pip pywayland requires gcc
2323
if [[ $(command -v apt-get 2>/dev/null) ]]; then
2424
PACKAGE_MANAGER="apt"
25-
sudo apt-get -y install ibus libevdev2 curl xinput i2c-tools python3-dev python3-virtualenv libxml2-utils libxkbcommon-dev gcc pkg-config
25+
sudo apt-get -y install ibus libevdev2 curl xinput i2c-tools python3-dev python3-virtualenv libxml2-utils libxkbcommon-dev gcc pkg-config libxcb-render0-dev
2626
sudo apt-get -y install libsystemd-dev
2727
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
2828
sudo apt-get -y install libwayland-dev
2929
fi
3030

3131
elif [[ $(command -v pacman 2>/dev/null) ]]; then
3232
PACKAGE_MANAGER="pacman"
33-
sudo pacman --noconfirm --needed -S ibus libevdev curl xorg-xinput i2c-tools python python-virtualenv libxml2 libxkbcommon gcc pkgconf
33+
sudo pacman --noconfirm --needed -S ibus libevdev curl xorg-xinput i2c-tools python python-virtualenv libxml2 libxkbcommon gcc pkgconf libxcb
3434
sudo pacman --noconfirm --needed -S systemd
3535
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
3636
sudo pacman --noconfirm --needed -S wayland
3737
fi
3838

3939
elif [[ $(command -v dnf 2>/dev/null) ]]; then
4040
PACKAGE_MANAGER="dnf"
41-
sudo dnf -y install ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config
41+
sudo dnf -y install ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config libxcb-devel
4242
sudo dnf -y install systemd-devel
4343
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
4444
sudo dnf -y install wayland-devel
4545
fi
4646

4747
elif [[ $(command -v yum 2>/dev/null) ]]; then
4848
PACKAGE_MANAGER="yum"
49-
sudo yum -y install ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config
49+
sudo yum -y install ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config libxcb-devel
5050
sudo yum -y install systemd-devel
5151
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
5252
sudo yum -y install wayland-devel
5353
fi
5454

5555
elif [[ $(command -v zypper 2>/dev/null) ]]; then
5656
PACKAGE_MANAGER="zypper"
57-
sudo zypper --non-interactive install ibus libevdev2 curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config
57+
sudo zypper --non-interactive install ibus libevdev2 curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config libxcb-devel
5858
sudo zypper --non-interactive install systemd-devel
5959
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
6060
sudo zypper --non-interactive install wayland-devel
6161
fi
6262

6363
elif [[ $(command -v xbps-install 2>/dev/null) ]]; then
6464
PACKAGE_MANAGER="xbps-install"
65-
sudo xbps-install -Suy ibus-devel libevdev-devel curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config
65+
sudo xbps-install -Suy ibus-devel libevdev-devel curl xinput i2c-tools python3-devel python3-virtualenv libxml2 libxkbcommon-devel gcc pkg-config libxcb-devel
6666
sudo xbps-install -Suy systemd
6767
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
6868
sudo xbps-install -Suy wayland-devel
6969
fi
7070

7171
elif [[ $(command -v emerge 2>/dev/null) ]]; then
7272
PACKAGE_MANAGER="portage"
73-
sudo emerge app-i18n/ibus dev-libs/libevdev net-misc/curl x11-apps/xinput sys-apps/i2c-tools dev-lang/python dev-python/virtualenv dev-libs/libxml2 x11-libs/libxkbcommon sys-devel/gcc virtual/pkgconfig
73+
sudo emerge app-i18n/ibus dev-libs/libevdev net-misc/curl x11-apps/xinput sys-apps/i2c-tools dev-lang/python dev-python/virtualenv dev-libs/libxml2 x11-libs/libxkbcommon sys-devel/gcc virtual/pkgconfig x11-libs/libxcb
7474
sudo emerge sys-apps/systemd
7575
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
7676
sudo emerge dev-libs/wayland
7777
fi
7878

7979
elif [[ $(command -v rpm-ostree 2>/dev/null) ]]; then
8080
PACKAGE_MANAGER="rpm-ostree"
81-
sudo rpm-ostree install xinput virtualenv python3-devel wayland-protocols-devel pkg-config
81+
sudo rpm-ostree install xinput virtualenv python3-devel wayland-protocols-devel pkg-config libxcb-devel
8282
sudo rpm-ostree install systemd-devel
8383
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
8484
sudo rpm-ostree install wayland-devel
8585
fi
8686

8787
elif [[ $(command -v eopkg 2>/dev/null) ]]; then
8888
PACKAGE_MANAGER="eopkg"
89-
sudo eopkg install -y ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2-devel libxkbcommon-devel gcc pkg-config
89+
sudo eopkg install -y ibus libevdev curl xinput i2c-tools python3-devel python3-virtualenv libxml2-devel libxkbcommon-devel gcc pkg-config libxcb-devel
9090
sudo eopkg install -y systemd-devel
9191
if [ "$XDG_SESSION_TYPE" == "wayland" ]; then
9292
sudo eopkg install -y wayland-devel

numberpad.py

Lines changed: 126 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
from pywayland.protocol.wayland import WlSeat
2424
import mmap
2525
from smbus2 import SMBus, i2c_msg
26-
import ast
2726
import signal
2827
import math
2928
import glob
29+
import xcffib
30+
import xcffib.xkb
3031

3132
GNOME_GLIB_AVAILABLE = False
3233

@@ -46,6 +47,7 @@
4647
display_wayland = None
4748
keyboard_state = None
4849
display = None
50+
xkb_conn = None
4951
keymap_loaded = False
5052
listening_touchpad_events_started = False
5153

@@ -69,6 +71,9 @@
6971
try:
7072
display = Xlib.display.Display(display_var)
7173
log.info("X11 detected and connected succesfully to the display {}".format(display_var))
74+
xkb_conn = xcffib.connect()
75+
xkb_conn_setup = xkb_conn.get_setup()
76+
log.info("X11 detected and connected succesfully to the xcffib")
7277
except:
7378
log.error("X11 detected but not connected succesfully to the display {}. Exiting".format(display_var))
7479
sys.exit(1)
@@ -257,43 +262,138 @@ def enable_key(key_or_key_combination, reset_udev = False):
257262
if len(enabled_evdev_keys) > enabled_keys_count and reset_udev:
258263
reset_udev_device()
259264

265+
def resolve_keycode_with_xcffib_xkb(keysym):
266+
global xkb_conn, xkb_conn_setup, xkb_active_group, xkb_map
267+
268+
try:
269+
min_keycode, max_keycode = xkb_conn_setup.min_keycode, xkb_conn_setup.max_keycode
270+
271+
key_sym_maps = getattr(xkb_map, "syms_rtrn", [])
272+
if not key_sym_maps:
273+
return (None, None)
274+
275+
for kc in range(min_keycode, max_keycode + 1):
276+
277+
# Iterate entries by index; keycode is min_kc + index
278+
for idx, entry in enumerate(key_sym_maps):
279+
kc = min_keycode + idx
280+
syms = getattr(entry, "syms", [])
281+
n = getattr(entry, "nSyms", len(syms))
282+
width = getattr(entry, "width", 2) # levels per group, fallback to 2
283+
284+
if not syms or n == 0 or width <= 0:
285+
continue
286+
287+
limit = min(n, len(syms))
288+
for i in range(limit):
289+
group = i // width
290+
if group != xkb_active_group:
291+
continue
292+
if syms[i] == keysym:
293+
level = i % width
294+
return (kc, level)
295+
296+
return (None, None)
297+
298+
except Exception:
299+
log.exception("resolve_keycode_with_xkb() failed unexpectedly")
300+
return (None, None)
301+
302+
def resolve_keycode_with_xlib(keysym):
303+
global display
304+
305+
keycode = display.keysym_to_keycode(keysym)
306+
if not keycode:
307+
return (None, None)
308+
309+
level = None
310+
for l in (0, 1, 2, 3, 4):
311+
if display.keycode_to_keysym(keycode, l) == keysym:
312+
level = l
313+
break
314+
315+
return (keycode, level)
316+
317+
def load_xkb_map_and_active_group():
318+
global xkb_conn, xkb_active_group, xkb_map
319+
320+
xkb = xkb_conn(xcffib.xkb.key)
321+
if not xkb.UseExtension(1, 0).reply().supported:
322+
return (None, None)
323+
324+
state = xkb.GetState(xcffib.xkb.ID.UseCoreKbd).reply()
325+
326+
# xkb has support up to 4 groups max
327+
xkb_active_group = state.group % 4
328+
329+
core_kbd_id = xkb.GetDeviceInfo(xcffib.xkb.ID.UseCoreKbd, 0, 0, 0, 0, 0, 0).reply().deviceID
330+
331+
components = (
332+
xcffib.xkb.MapPart.KeyTypes |
333+
xcffib.xkb.MapPart.KeySyms |
334+
xcffib.xkb.MapPart.ModifierMap |
335+
xcffib.xkb.MapPart.ExplicitComponents |
336+
xcffib.xkb.MapPart.KeyActions |
337+
xcffib.xkb.MapPart.VirtualMods |
338+
xcffib.xkb.MapPart.VirtualModMap
339+
)
340+
341+
xkb_map = xkb.GetMap(
342+
core_kbd_id,
343+
components,
344+
0, 0, 0, 0,
345+
0, 0, 0, 0,
346+
0, 0, 0, 0,
347+
0, 0, 0, 0,
348+
).reply()
260349

261350
def load_evdev_key_for_x11(char):
262351
global display, keysym_name_associated_to_evdev_key_reflecting_current_layout
263352

264353
keysym = Xlib.XK.string_to_keysym(char)
265-
266354
if keysym == 0:
267-
return
355+
return
356+
357+
# try xkb via xcffib first
358+
keycode, level = resolve_keycode_with_xcffib_xkb(keysym)
359+
360+
# fallback to python-xlib (does not ship an xkb module)
361+
if keycode is None:
362+
keycode, level = resolve_keycode_with_xlib(keysym)
363+
if keycode is None or level is None:
364+
return
268365

269-
keycode = display.keysym_to_keycode(keysym)
270366
key = EV_KEY.codes[int(keycode) - 8]
271367

272-
# bare
273-
if display.keycode_to_keysym(keycode, 0) == keysym:
274-
pass
275-
# shift
276-
elif display.keycode_to_keysym(keycode, 1) == keysym:
277-
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('Shift')), key]
278-
# altgr
279-
elif display.keycode_to_keysym(keycode, 2) == keysym:
280-
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('AltGr')), key]
281-
# shift altgr
282-
elif display.keycode_to_keysym(keycode, 3) == keysym:
283-
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('Shift')), load_evdev_key_for_x11(mod_name_to_specific_keysym_name('AltGr')), key]
368+
if level == 0:
369+
pass
370+
elif level == 1: # Shift
371+
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('Shift')), key]
372+
elif level == 2: # AltGr
373+
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('AltGr')), key]
374+
elif level == 3: # Shift + AltGr
375+
key = [
376+
load_evdev_key_for_x11(mod_name_to_specific_keysym_name('Shift')),
377+
load_evdev_key_for_x11(mod_name_to_specific_keysym_name('AltGr')),
378+
key
379+
]
380+
elif level == 4: # Level5 (ISO_Level5_Shift)
381+
key = [load_evdev_key_for_x11(mod_name_to_specific_keysym_name('Meta')), key]
284382

285383
set_evdev_key_for_char(char, key)
286-
287384
enable_key(key)
288-
289385
return key
290386

291-
292387
def load_evdev_keys_for_x11():
293388
global enabled_evdev_keys, keymap_loaded, udev
294389

295390
log.debug("X11 will try to load keymap")
296391

392+
try:
393+
load_xkb_map_and_active_group()
394+
except:
395+
pass
396+
297397
enabled_keys_count = len(enabled_evdev_keys)
298398

299399
for char in get_keysym_name_associated_to_evdev_key_reflecting_current_layout().copy():
@@ -2414,7 +2514,6 @@ def check_touchpad_status():
24142514

24152515
numlock_lock.release()
24162516

2417-
24182517
def check_system_numlock_status():
24192518
global stop_threads
24202519

@@ -2514,7 +2613,7 @@ def check_config_values_changes():
25142613

25152614

25162615
def cleanup():
2517-
global numlock, is_idled, display, display_wayland, stop_threads, event_notifier, watch_manager
2616+
global numlock, is_idled, display, display_wayland, stop_threads, event_notifier, watch_manager, xkb_conn
25182617

25192618
log.info("Clean up started")
25202619

@@ -2552,6 +2651,12 @@ def cleanup():
25522651
except:
25532652
pass
25542653

2654+
try:
2655+
if xkb_conn is not None:
2656+
xkb_conn.disconnect()
2657+
except Exception:
2658+
pass
2659+
25552660
if watch_manager:
25562661
watch_manager.close()
25572662

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ python-xlib
55
smbus2
66
pyasyncore
77
pywayland
8-
xkbcommon<1.1
8+
xkbcommon<1.1
9+
xcffib

0 commit comments

Comments
 (0)