make mappings like a::b and b::a work #42

Open
madewokherd opened this Issue Nov 30, 2011 · 1 comment

Projects

None yet

2 participants

@madewokherd
Contributor

This is mainly an excuse to get down some thoughts about the X protocol and how to use it to do these crazy things. Maybe you guys have already figured this stuff out.

As a concrete example, consider the following script:

a::b
b::a
CapsLock & B::Send, Bacon

In order to make this work properly, we need to block "a" and "b" keypresses from the user, in all cases, from arriving at their destinations. This is fairly easy to do; use XGrabKey. However, our own events will also trigger XGrabKey, and we need to make sure they go through.

So, here's what I have so far:

  • First, grab all our hotkeys on the root window with XGrabKey and GrabModeSync.
  • When we get a keypress from our passive grab, make a note that we now have effectively grabbed the keyboard, and make a note of all the keypresses we get without a corresponding keyrelease during that effective grab. The corresponding keyreleases will go to our client so that keyups and keyreleases always match.
  • When we block an event that we did not Send (for various reasons because of a passive grab), decide whether that event should go through to other applications. If not, call XAllowEvents(SyncKeyboard). If we do want the event to go through, call XAllowEvents(ReplayKeyboard).
  • To Send an event, use XTestFakeKeyEvent to simulate the appropriate event. Make a note of the sequence of events we've simulated so we can ignore our own events.
    • When we get an event that we haven't simulated, clear our list of simulated events.
    • In some cases, such as CapsLock & B, we'll have to grab keys temporarily in response to other passive grabs that trigger.

Problems:

  • I don't know whether it's possible to grab the keyboard using XGrabKeyboard while it is frozen. If it's not possible, we won't be able to activate menus using hotkey presses.
  • We still can't get any events during an active grab. AFAIK that's unsolveable.
  • This may have some race conditions. For example, if we simulate an event on a key release that triggers a menu, followed by keypresses that would go to one of our passive grabs, our passive grab may never trigger (because menus actively grab the keyboard). If the user later presses that passively grabbed key, without any other input in between, we won't be able to tell that the keypress wasn't caused by us. Conversely, we can't be sure that all our simulated input will go through before user input.
  • I'm unsure of whether we'll get keyup events that correspond to keydown events that we replay during a passive grab.
  • Some combinations can still cause problems because a key can't be in two logical states at once. I.e. if we simulate a keyup in response to a keydown, we have no way of finding out when the real keyup happens. This also means that the remappings for "a" and "b" above cannot be used at the same time. We could try to simulate some events with XSendEvent instead, but that has its own problems.

I saw talk in another issue of using at-spi2 for keyboard events, but I don't think it will get us much in that areas. In particular, I don't think it will allow us to block keyboard events from being delivered when we need to. In any case, these ideas are not mutually exclusive; we can use X to grab keys and get events during passive keyboard grabs, and at-spi2 to get them the rest of the time. Maybe it will provide a way to simulate events without sending them through X, which could help, but I doubt it. (That said, at-spi2 allows for manipulating guis in a lot of other ways, which is awesome.)

@TobiasKappe
Collaborator

Reading through your description, it appears that you know a lot about X11, a lot more than I do, in fact. If you feel that your approach would work then please be my guest and try to implement it. Other than that, there are a few points I'd like to remark upon

  • While X does give us a lot of control over sending and receiving events, it does not give us total control over what happens to the keyboard. Programs known to act differently include Gnome Do (maybe they do keyboard grabs too?) and Firefox. Programs may also choose to ignore simulated keypress events.
  • There are indeed very real race conditions that may occur in any realistic tackling of this problem. This is mainly caused by X protocol overhead. Perhaps Wayland will provide a more robust/fast protocol, but until it is as ubiquitous as X we'd better cater to X.
  • The main reason I suspended work on hotkey/hotstring implementation was that I hit a dead end using X (largely due to lack of knowledge). My hope was that at-spi2 would be included in Gnome 3 and it'd make my efforts much easier by providing a D-Bus interface and, as you say, the possibility of actually manipulating GUIs. Mind you, I still have to check if it actually is included. However, I do know that communicating with at-spi as present in most Linux distribution is hard to get right from C#. I did manage to write a python script that uses the pyatspi. Said script was capable of blocking keyboard events - I looked it up, but it appears I have deleted it.

So in summary, if you want to retry an approach using X, be my guest. Judging from your plan there's even a good chance you'll succeed. If you run into issues I'll be more than happy to provide assistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment