JSubelj / g910-gkey-macro-support

GKey support for Logitech G910 Keyboard on Linux
GNU General Public License v3.0
99 stars 31 forks source link

[Issue] Modifier does not work for other G-Keys #15

Open braoult opened 5 years ago

braoult commented 5 years ago

Hi,

I want to use G5 a an "Hyper" Key, and all other keys G-keys defined as F-key (F13...F21). My main point is that I prefer to manage G-keys at application-level. Note: Below was tested on XUbuntu 18.04.

I added in char_uinput_mapper.py :

        # japanese keys
        "muhenkan": click(uinput.KEY_MUHENKAN),
        "zenkakuhankaku": click(uinput.KEY_ZENKAKUHANKAKU),
        "katakana": click(uinput.KEY_KATAKANA),
        "hiragana": click(uinput.KEY_HIRAGANA),
        "henkan": click(uinput.KEY_HENKAN),
        "katakanahiragana": click(uinput.KEY_KATAKANAHIRAGANA),
        'F19': click(uinput.KEY_F19),
        'F20': click(uinput.KEY_F20),
        'F21': click(uinput.KEY_F21),
        'F22': click(uinput.KEY_F22),
        'F23': click(uinput.KEY_F23),
        'F24': click(uinput.KEY_F24),

My /etc/g910-gkeys/config.json contains the mapping of G1-G9 to F13-F21 (please see the comment for G8) :

{
    "__comment": "following hotkey types are supported: nothing, typeout, shortcut and run; only en and si keyboard mappings are currently supported",
    "keyboard_mapping": "en",
    "g1": {
        "hotkey_type": "shortcut",
        "do": "F13"
    },
    "g2": {
        "hotkey_type": "shortcut",
        "do": "F14"
    },
    "g3": {
        "hotkey_type": "shortcut",
        "do": "F15"
    },
    "g4": {
        "hotkey_type": "shortcut",
        "do": "F16"
    },
    "g5": {
        "hotkey_type": "shortcut",
        "do": "F17"
    },
    "g6": {
        "hotkey_type": "shortcut",
        "do": "F18"
    },
    "g7": {
        "hotkey_type": "shortcut",
        "do": "F19"
    },
   "__comment": "I am in fact unable to use F20 : even xev does not show the keycode. It just mutes/unmutes sound, same as the G910's <mute> button - until understood, F21&F22 look a better choice below",
    "g8": {
        "hotkey_type": "shortcut",
        "do": "F20"
    },
    "g9": {
        "hotkey_type": "shortcut",
        "do": "F21"
    }
}

I have added in my ~.Xmodmap :

! Map "Windows right" to Meta_R and "Menu right" to Super_R
keycode 134 = Meta_R NoSymbol Meta_R
keycode 135 = Super_R NoSymbol Super_R
! and G5 (F17) becomes Hyper_L
keycode 195 = Hyper_L NoSymbol Hyper_L

! necessary on my system, for some reason original modifier for mod4 contains already Hyper_L:
! mod4        Super_L (0x85),  Meta_R (0x86),  Super_L (0xce),  Hyper_L (0xcf)
clear mod4
add mod4 = Super_L Super_R

! now we define G5 as Hyper_L
clear mod3
add mod3 = Hyper_L

!!!!!!!!!!!!!! Logitech G910 keyboard
! original F13, F14, etc... mapping
! F13 = keycode 191 = XF86Toolks NoSymbol XF86Tools
! F14 = keycode 192 = XF86Launch5 NoSymbol XF86Launch5
! F15 = keycode 193 = XF86Launch6 NoSymbol XF86Launch6
! F16 = keycode 194 = XF86Launch7 NoSymbol XF86kLaunch7
! F17 = keycode 195 = XF86Launch8 NoSymbol XF86Launch8
! F18 = keycode 196 = XF86Launch9 NoSymbol XF86Launch9
! F19 = keycode 197 = (nothing)
! F20 = keycode 198 = XF86AudioMicMute NoSymbol XF86AudioMicMute
! F21 = keycode 199 = XF86TouchpadToggle NoSymbol XF86TouchpadToggle
! F22 = keycode 200 = XF86TouchpadOn NoSymbol XF86TouchpadOn

! map the Gkeys to F13-F22, and not XF86Tools, etc...
! Except G5, that became Hyper_L above
! Keycodes are from 191 (G1/F13) to 199 (G9/F21)
keycode 191 = F13 F13 F13
keycode 192 = F14 F14 F14
keycode 193 = F15 F15 F15
keycode 194 = F16 F16 F16
! G5 - skipped
! keycode 195 = F17  F17 F17
keycode 196 = F18 F18 F18
keycode 197 = F19 F19 F19
keycode 198 = F20 F20 F20
keycode 199 = F21 F21 F21

So far so good, the hyper modifier works (tested under Emacs): G5-c is seen as Hyper-c, etc... G5+CTRL+Windows+ALT+SHIFT-f is seen as ctrl-meta-super-hyper-shift-f.

For other g-keys (except G8/F20), I can also use ctrl-g1, shift-g1, meta-g1, ctrl+super+g1 etc, who return the correct codes (ctrl-F13, etc...).

The issue is for the "hyper" modifier (G5), when used with another G-KEY. G5-G1 should return "Hyper-F13". It returns nothing. More interesting: When used with another modifier (let say control), nothing is returned too: ctrl-G5-G1, shift-G5-G1 etc... do not work.

Hope this will help,

br.

JSubelj commented 5 years ago

Hey,

you really went to great lengths to investigate the issue. I really appreciate it.

Currently the GKeys can't be used in combinations (G1+G2...) because the keyboard returns different hex values. As you can see here command_bytearray.py, every GKey is maped to a unique hex byte array. But when you press multiple GKeys or press and hold G1 and then press G2 the keyboard returns a completely different hex byte array. This is probably because it merges the two hex codes from G1 and G2 together (my guess is it does some kind of boolean operation on them).

Because of this behavior the driver currently simply drops the hex code it receives because it does not know what to do with it. Implementing the decoding will be quite tedious but I hope I will find some time to code it in when I figure out how the Gkeys are actually merged. (Nothing is easy with Logitech on Linux :P)

mvastola commented 4 years ago

So I just found this project (which is awesome) and I saw this issue. Doing a very simple check of command_bytearray.py (literally converting everything to pure hex and adding spaces), this operation would appear (emphasis here) to be extremely straightforward:

# import binascii
# spaces added below
b'11 ff 08 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g1'])
b'11 ff 08 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g2'])
b'11 ff 08 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g3'])
b'11 ff 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g4'])
b'11 ff 08 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g5'])
b'11 ff 08 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g6'])
b'11 ff 08 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g7'])
b'11 ff 08 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g8'])
b'11 ff 08 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['g9'])

b'11 ff 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['m1'])
b'11 ff 09 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['m2'])
b'11 ff 09 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['m3'])
b'11 ff 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['m1-3_key_release'])

b'11 ff 0a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['mr'])
b'11 ff 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' # >>> binascii.hexlify(commands['mr-key_release'])

Basically, at least for the Gx keys, they always have the first three bytes as 0x11ff08. Beyond that, each of the G keys has exactly one bit set, and that bit is unique among all of the Gx keys. The obvious conclusion from this is that the Gx keys are signaled this way so that they can be 'OR'ed together when pressed in unison, and can be used as a mask to distinguish the combination of keys pressed.

Note I'm not sure how this would work for a Gx key and a normal key at the same time, but my guess would be that the byte sent by the normal key is 'OR'ed with a one of the 0x00 bytes in the bytearray.

For the Mx keys and the MR key, since there is a release value, my guess is that these aren't combined at all (though the Mx keys could be combined with each other). For Mx when a bytearray begins with 0x11ff09, this is a signal to set a register indicating which (if any) Mx keys are active. The value of the register becomes the bits that follow. This fits into the idea that the release key for M1 through M3 turns off all of those bits. Similarly, MR would seem to be the same, but with just one key that is set to either 1 or 0.

I'm going to try this out when I get a chance, unless someone else wants to. I'll report back.

mvastola commented 4 years ago

Oh. It looks like someone in #32 already determined the values of the actual arrays produced from the key combos. I just combined it what I did, and it seems to be 'OR'ing it. I'll see if I can make a simple PR.

If it's of any use, code I used is attached. gkeycombos.rb.txt