|
23 | 23 | from pywayland.protocol.wayland import WlSeat |
24 | 24 | import mmap |
25 | 25 | from smbus2 import SMBus, i2c_msg |
26 | | -import ast |
27 | 26 | import signal |
28 | 27 | import math |
29 | 28 | import glob |
| 29 | +import xcffib |
| 30 | +import xcffib.xkb |
30 | 31 |
|
31 | 32 | GNOME_GLIB_AVAILABLE = False |
32 | 33 |
|
|
46 | 47 | display_wayland = None |
47 | 48 | keyboard_state = None |
48 | 49 | display = None |
| 50 | +xkb_conn = None |
49 | 51 | keymap_loaded = False |
50 | 52 | listening_touchpad_events_started = False |
51 | 53 |
|
|
69 | 71 | try: |
70 | 72 | display = Xlib.display.Display(display_var) |
71 | 73 | 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") |
72 | 77 | except: |
73 | 78 | log.error("X11 detected but not connected succesfully to the display {}. Exiting".format(display_var)) |
74 | 79 | sys.exit(1) |
@@ -257,43 +262,138 @@ def enable_key(key_or_key_combination, reset_udev = False): |
257 | 262 | if len(enabled_evdev_keys) > enabled_keys_count and reset_udev: |
258 | 263 | reset_udev_device() |
259 | 264 |
|
| 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() |
260 | 349 |
|
261 | 350 | def load_evdev_key_for_x11(char): |
262 | 351 | global display, keysym_name_associated_to_evdev_key_reflecting_current_layout |
263 | 352 |
|
264 | 353 | keysym = Xlib.XK.string_to_keysym(char) |
265 | | - |
266 | 354 | 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 |
268 | 365 |
|
269 | | - keycode = display.keysym_to_keycode(keysym) |
270 | 366 | key = EV_KEY.codes[int(keycode) - 8] |
271 | 367 |
|
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] |
284 | 382 |
|
285 | 383 | set_evdev_key_for_char(char, key) |
286 | | - |
287 | 384 | enable_key(key) |
288 | | - |
289 | 385 | return key |
290 | 386 |
|
291 | | - |
292 | 387 | def load_evdev_keys_for_x11(): |
293 | 388 | global enabled_evdev_keys, keymap_loaded, udev |
294 | 389 |
|
295 | 390 | log.debug("X11 will try to load keymap") |
296 | 391 |
|
| 392 | + try: |
| 393 | + load_xkb_map_and_active_group() |
| 394 | + except: |
| 395 | + pass |
| 396 | + |
297 | 397 | enabled_keys_count = len(enabled_evdev_keys) |
298 | 398 |
|
299 | 399 | for char in get_keysym_name_associated_to_evdev_key_reflecting_current_layout().copy(): |
@@ -2414,7 +2514,6 @@ def check_touchpad_status(): |
2414 | 2514 |
|
2415 | 2515 | numlock_lock.release() |
2416 | 2516 |
|
2417 | | - |
2418 | 2517 | def check_system_numlock_status(): |
2419 | 2518 | global stop_threads |
2420 | 2519 |
|
@@ -2514,7 +2613,7 @@ def check_config_values_changes(): |
2514 | 2613 |
|
2515 | 2614 |
|
2516 | 2615 | 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 |
2518 | 2617 |
|
2519 | 2618 | log.info("Clean up started") |
2520 | 2619 |
|
@@ -2552,6 +2651,12 @@ def cleanup(): |
2552 | 2651 | except: |
2553 | 2652 | pass |
2554 | 2653 |
|
| 2654 | + try: |
| 2655 | + if xkb_conn is not None: |
| 2656 | + xkb_conn.disconnect() |
| 2657 | + except Exception: |
| 2658 | + pass |
| 2659 | + |
2555 | 2660 | if watch_manager: |
2556 | 2661 | watch_manager.close() |
2557 | 2662 |
|
|
0 commit comments