Why XGrabKey generates extra focus-out and focus-in events?

后端 未结 7 2099
时光说笑
时光说笑 2020-12-14 00:33

Does anyone know an xlib function to trap a keypress event without losing the original focus? How to get rid of it?

(or \"to use XGrabKey() without generating Grab-s

7条回答
  •  别那么骄傲
    2020-12-14 01:14

    I've got an idea that I'm pretty sure would work, but I have to get to bed and can't test it myself, and it isn't pretty, since I don't think there's any way to do what you want in X. Here's the steps I have in mind. In short: disable the keyboard in X, read the events from the lower level api, and selectively feed them to X yourself. You have to disable the keyboard in X because otherwise, you could look at the event, but not stop it; you would read it alongside X, not intercept it.

    So here it is broken out:

    1) Run xinput -list to get the keyboard X is using

    2) Run xinput list-props id to find the Device Enabled property

    3) Run xinput set-prop id prop 0 to disable the device in X:

    xinput -list
    xinput list-props 12 # 12 is the id of the keyboard in the list... (example # btw)
    xinput set-prop 12 119 0 # 119 is the "Device Enabled" prop, we turn it off
    

    I don't know how xinput works on the xlib level, I'd just call it out to the shell for simplicity of implementation.

    4) Open /dev/input/eventX, where X is the keyboard device. I'd actually search for the name (given in xinput -list) under /dev/input/by-id and open it that way. This will likely require root at some point, since the permissions on these are generally pretty restrictive.

    5) Read the keyboard input from there:

    The format of the data from the input events is:

    struct input_event {
        int tv_sec; // time of the event
        int tv_usec; // ditto
        ushort type; // == 1 for key event
        ushort code; // key code, not the same as X keysyms, you should check it experimentally. 42 is left shift on mine, 54 is right shift
        int value; // for keys, 1 == pressed, 0 == released, 2 == repeat
    }
    

    ints are 32 bit, ushorts are 16 bit. Since you're only interested in keyboard input, you could do this pretty simply:

    • read and ignore 8 bytes.

    • the next byte should be 1, then the next one is 0. if not, skip this event

    • Next byte is the little end of the key code, and since there's < 255 keys, that's good enough so

    • skip the next byte.

    • read the next byte to see if itis pressed or released

    • skip the next three bytes

    6) When you get an event you're interested in trapping, handle it yourself. Otherwise, use XSendEvent to send it to X so it can be processed normally. Mapping the hardware code you get from /dev/input to the appropriate keysym might be a trick, but I'm fairly certain there's a function in xlib somewhere to help with this.

    7) goto 5 and loop till you're done

    8) Make sure you set everything back to how it was when you exit, or you could break the user's keyboard input to X!

    I'd suggest testing this with a second usb keyboard, you can disable and listen to keyboards independently of each other with /dev/input and xinput, so if you crash, you still have the first keyboard in and working normally. (Actually, I think it'd be pretty cool to intentionally do it with a second keyboard, double the hotkeys!)

    But yeah, needing root and potentially leaving the keyboard "detached" from X aren't pretty at all, and that forwarding-with-SendKey might be easier said than done, but I'm pretty sure this would work and give you maximum flexibility.

提交回复
热议问题