From 5e4892c35d73ab2a633b06cf5189de5088617fd4 Mon Sep 17 00:00:00 2001 From: Stephen Early Date: Tue, 9 Apr 2024 23:17:15 +0100 Subject: [PATCH] Implement Keymap.key_get_mods_for_level() This API was introduced in libxkbcommon-1.0 Remove ubuntu-20.04 from CI because this only has libxkbcommon-0.8 Remove Python 3.6 and 3.7 from CI because they are no longer supported. --- .github/workflows/run-tests.yml | 7 ++----- tests/test_xkb.py | 9 +++++++++ xkbcommon/ffi_build.py | 10 +++++++++- xkbcommon/xkb.py | 23 +++++++++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9fa3295..79bfb3f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -7,11 +7,8 @@ jobs: strategy: matrix: - os: ['ubuntu-20.04', 'ubuntu-22.04'] - python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] - exclude: - - os: 'ubuntu-22.04' - python-version: '3.6' + os: ['ubuntu-22.04'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] runs-on: ${{ matrix.os }} diff --git a/tests/test_xkb.py b/tests/test_xkb.py index 089dbdf..6feea37 100644 --- a/tests/test_xkb.py +++ b/tests/test_xkb.py @@ -252,6 +252,15 @@ def test_keymap_num_levels_for_key(self): key = next(iter(self.km)) self.assertEqual(self.km.num_levels_for_key(key, 0), 1) + def test_keymap_key_get_mods_for_level(self): + first_invalid_mask = 1 << self.km.num_mods() + for key in self.km: + for layout in range(self.km.num_layouts_for_key(key)): + for level in range(self.km.num_levels_for_key(key, layout)): + r = self.km.key_get_mods_for_level(key, layout, level) + for mm in r: + self.assertLess(mm, first_invalid_mask) + def test_keymap_key_get_syms_by_level(self): for key in self.km: r = self.km.key_get_syms_by_level(key, 0, 0) diff --git a/xkbcommon/ffi_build.py b/xkbcommon/ffi_build.py index fa7db92..c222655 100644 --- a/xkbcommon/ffi_build.py +++ b/xkbcommon/ffi_build.py @@ -1,7 +1,7 @@ from cffi import FFI ffibuilder = FFI() -# Currently implemented with reference to libxkbcommon-0.6.0 +# Currently implemented with reference to libxkbcommon-1.0.0 ffibuilder.set_source("xkbcommon._ffi", """ #include @@ -253,6 +253,14 @@ xkb_keymap_num_levels_for_key(struct xkb_keymap *keymap, xkb_keycode_t key, xkb_layout_index_t layout); +size_t +xkb_keymap_key_get_mods_for_level(struct xkb_keymap *keymap, + xkb_keycode_t key, + xkb_layout_index_t layout, + xkb_level_index_t level, + xkb_mod_mask_t *masks_out, + size_t masks_size); + int xkb_keymap_key_get_syms_by_level(struct xkb_keymap *keymap, xkb_keycode_t key, diff --git a/xkbcommon/xkb.py b/xkbcommon/xkb.py index 155539e..a97d914 100644 --- a/xkbcommon/xkb.py +++ b/xkbcommon/xkb.py @@ -618,6 +618,29 @@ def num_levels_for_key(self, key, layout): """ return lib.xkb_keymap_num_levels_for_key(self._keymap, key, layout) + def key_get_mods_for_level(self, key, layout, level): + """Retrieves every possible modifier mask that produces the specified + shift level for a specific key and layout. + + This API is useful for inverse key transformation; + i.e. finding out which modifiers need to be active in order to + be able to type the keysym(s) corresponding to the specific + key code, layout and level. + + If layout is out of range for this key (that is, larger or + equal to the value returned by Keymap.num_layouts_for_key()), + it is brought back into range in a manner consistent with + State.key_get_layout(). + """ + masks_size = 4 + while True: + masks_out = ffi.new(f"xkb_mod_mask_t[{masks_size}]") + r = lib.xkb_keymap_key_get_mods_for_level( + self._keymap, key, layout, level, masks_out, masks_size) + if r < masks_size: + return [masks_out[n] for n in range(r)] + masks_size = masks_size << 1 + def key_get_syms_by_level(self, key, layout, level): """Get the keysyms obtained from pressing a key in a given layout and shift level.