JSubelj / g910-gkey-macro-support

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

PyUSB - re-attach kernel driver and new layout helper #71

Closed suabo closed 10 months ago

suabo commented 1 year ago

[BUGFIX]

[IMPROVMENTS]

suabo commented 1 year ago

Since sending control messages to the keyboard without knowing what they do seamed a little odd to me I decided to try it without and couldn't mention any other behavior then if I send the codes on initialization. Maybe the g-key mode gets saved to the keyboard so I didn't experience any difference now, but would if I have started the driver for the first time? Or maybe this was just a feature of the early versions of the keyboard? Which f-keys are the g-keys bound if the hardware mapping is active? Do the g-keys send out other bytearrays if in the other mode?

I would like to know exactly what the difference would be, before re-implementing the code for it.

Maybe I will boot up my Windows some time soon to see if i have some option like that in the windows driver and can set it back to look how it reacts and maybe figure out a little bit more about it.

suabo commented 1 year ago

I have some news on the g-key mode: I figured out on my keyboard G910OrionSpectrum this command will enable g-key mode:

payload = b'\x11\xff\x08\x2e\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
self.dev.ctrl_transfer(0x21, 0x09, 0x0212, 1, payload, self.usb_timeout)

After the command is send there is six packets send as confirmation:

array('B', [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) <- ? request type ? 21 bytes (one byte more then the others)
array('B', [2, 0]) <- media key release / request id
array('B', [17, 255, 8, 46, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) <- reflection of what we send
array('B', [17, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) <- gkey release
array('B', [17, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) <- mkey release
array('B', [17, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) <- mrkey release

This will bind g-keys to interface 1 for custom mapping. This is what we need for the driver.

You can also disable the mode:

payload = b'\x11\xff\x08\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
self.dev.ctrl_transfer(0x21, 0x09, 0x0212, 1, payload self.usb_timeout)

This will bind the g-keys to the interface 0 and f-keys (1-9). The response is identical with the one on enable.

How I thought: The g-key mode will persist in keyboard memory (didn't test if it persists on disconnect and reconnect)!

I optimized and re-implemented the enabling of g-key mode, since it is important for the driver to work. Now the driver checks for all the packets getting send by the keyboard like explained before. Before this there was some packets left and getting interpreted from the started service. This could lead to some unexpected behavior, but in the test case it is only release events for the keys, so it doesn't have an effect.

I thought about implementing this as a command line option (mainly for devs) and disabling the g-key mode on uninstall?

suabo commented 1 year ago

Thinking a little more about the g-key mode it would be nice to spoof the traffic on usb working with the windows driver to maybe implement it in profile switching. I'm pretty sure the assignment of keys/commands is handled in the driver like in ours but the set profile gets saved to the keyboard. I could imagine there is up to 254 profiles to use defined by the fifth byte, don't counting 0 as default. I only need to get the command to read what profile is set in keyboard to implement this. The other advancement could be made is to only set g-key mode if necessary. People using multiple OS on the same machine and also using the windows driver would also profit, because the profile wouldn't be reset to profileID 1 all the time the linux driver startet (just assuming this is the case right now).