Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to read used keysyms/keycodes for each modifier_map #480

Closed
ldrahnik opened this issue Apr 14, 2024 · 7 comments
Closed

How to read used keysyms/keycodes for each modifier_map #480

ldrahnik opened this issue Apr 14, 2024 · 7 comments

Comments

@ldrahnik
Copy link

ldrahnik commented Apr 14, 2024

I do not know how I can read keysyms or keycodes for each modifier_map below. Do I understand well used keysym are substitutable? Modifier Shift means can be pressed left or right Shift. I only care about first keysym in case previous statement is true. I know that func keymap.get_as_string() below returns at the end of output what I need but I would like to avoid using regexp and have another table translating LFSH -> Shift_L, RTSH -> Shift_R for which then I could easily find keycode using func xkb.keysym_from_name and iterating over all keycodes inside keymap and comparing keysym for first level but I would prefer something similar to Xlib example below and I have not found an equivalent func inside KeyboardState:

Output of keymap.get_as_string():

...
        modifier_map Shift { <LFSH>, <RTSH> };
        modifier_map Lock { <CAPS> };
        modifier_map Control { <LCTL>, <RCTL> };
        modifier_map Mod1 { <LALT>, <RALT>, <META> };
        modifier_map Mod2 { <NMLK> };
        modifier_map Mod4 { <LWIN>, <RWIN>, <SUPR>, <HYPR> };
        modifier_map Mod5 { <LVL3>, <MDSW> };

Using Xlib I can do something like:

    mods = display.get_modifier_mapping()
    mod_shift = mods[Xlib.X.ShiftMapIndex]
    first_keycode_for_mod_shift = mod_shift[0]
	second_keycode_for_mod_shift = mod_shift[1]
    key = EV_KEY.codes[int(first_keycode_for_mod_shift) - 8]
    # key -> KEY_LEFTSHIFT
    key = EV_KEY.codes[int(second_keycode_for_mod_shift) - 8]
    # key -> KEY_RIGHTSHIFT
...

Could help me anyone with a better understanding, please.

@wismill
Copy link
Member

wismill commented Apr 15, 2024

Duplicate of #91. TL;DR: There is no such API.

Could you explain you use case?

@ldrahnik
Copy link
Author

ldrahnik commented Apr 16, 2024

I develop a driver for Asus laptops with hardware support for NumberPad. NumberPad is an illuminated numeric keypad integrated to the touchpad which appears when is done tap on the top right corner of the touchpad. In this branch of driver I try to add support for rebinding modifiers as well as other keys for wayland. For Xlib I will solve it with code as I posted in issue probably because atm does not exist bindings for this lib for python supporting x11. Atm I have in the mentioned WIP branch static table below which I would like to change to be filled dynamically (Shift might have someone spared to some other keys than are Shift_L or Shift_R for example):

    mods_to_evdev_key_names = {
        'Control': 'Control_L',
        'Shift': 'Shift_L',
        'Lock': 'Caps_Lock',
        'Mod1': '', # TODO:
        'Mod2': '', # TODO:
        'Mod3': '', # TODO:
        'Mod4': '', # TODO:
        'Mod5': '', # TODO:
        'NumLock': 'Num_Lock',
        'Alt': 'Alt_L',
        'LevelThree': '', # TODO:
        'LAlt': 'Alt_L',
        'RAlt': 'Alt_R',
        'RControl': 'Control_R',
        'LControl': 'Control_L',
        'ScrollLock': 'Scroll_Lock',
        'LevelFive': '', # TODO:
        'AltGr': 'Alt_R',
        'Meta': 'Meta_L',
        'Super': 'Meta_L',
        'Hyper': 'Hyper_L'
    }

A concrete example is when the user wants using this driver via NumberPad send percent, it is on some layouts as shift+5. But when is shift not Shift_L? Even when I would send all characters via Unicode shortcut (shift+ctrl+U+<0-F>) modifiers shift, ctrl, U and enter or space for confirming have to be found dynamically.

@ldrahnik
Copy link
Author

@wismill What do you think? Should I try to implement the workaround solution mentioned in #91? About the proposed modification mentioned in #91 something like that does what I need. By index in keymap find all keycodes and return them thought pointer and as return value return the length of the keycodes array.

@wismill
Copy link
Member

wismill commented Apr 24, 2024

The NumberPad is a very interesting feature! As for your use case: there is not such API yet so yeah you would need to implement a workaround. Keep in mind that the corresponding keys depends on the active layout.

@ldrahnik
Copy link
Author

ldrahnik commented Apr 25, 2024

I wrote the code that returns for Shift -> Shift_L and the correct keycode 50 but I obtained for Shift Escape and keycode 9 too and not Shift_R. Why? What is in the code below wrong?

code

		mod_name = 'Shift' # or mod_name = 'Control'

        keymap = keyboard_state.get_keymap()
        num_mods = keymap.num_mods()
 
        for keycode in keymap:
            key_state = keyboard_state.update_key(keycode, xkb.KeyDirection.XKB_KEY_DOWN)
 
            num_layouts = keymap.num_layouts_for_key(keycode)
            for layout in range(0, num_layouts):
 
               layout_is_active = keyboard_state.layout_index_is_active(layout, xkb.StateComponent.XKB_STATE_LAYOUT_EFFECTIVE)
 
                if layout_is_active:
                    for mod_index in range(0, num_mods):
 
                        is_key_mod = key_state | xkb.StateComponent.XKB_STATE_MODS_DEPRESSED
                        if is_key_mod:
 
                            is_mod_active = keyboard_state.mod_index_is_active(mod_index, xkb.StateComponent.XKB_STATE_MODS_DEPRESSED)
                            if is_mod_active:
                                if keymap.mod_get_name(mod_index) == mod_name:
 
                                    keysyms = keymap.key_get_syms_by_level(keycode, layout, 0)
 
                                    if len(keysyms) != 1:
                                        continue
 
                                    keysym_name = xkb.keysym_get_name(keysyms[0])
                                    print(mod_name)
                                    print(keycode)
                                    print(keysym_name)
                                    return keysym_name
 
            keyboard_state.update_key(keycode, xkb.KeyDirection.XKB_KEY_UP)

output

Shift
50
Shift_L
Shift
9
Escape

When I find for Control, the same, I get Control_L and Escape too but not Control_R.

I use wrapper.

@wismill
Copy link
Member

wismill commented Apr 26, 2024

I do not know this library but the following might help:

  • You should ensure to reset the keyboard_state between each key press.
  • This seems a typo:
    -is_key_mod = key_state | xkb.StateComponent.XKB_STATE_MODS_DEPRESSED
    +is_key_mod = key_state & xkb.StateComponent.XKB_STATE_MODS_DEPRESSED

@ldrahnik
Copy link
Author

ldrahnik commented Apr 26, 2024

Thank you. With 2 mentioned changes I get now for Shift correctly:

output

Shift
50
Shift_L
Shift
62
Shift_R

@wismill wismill closed this as completed Apr 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants