I didn’t expect this to be a thing that is only fixed recently. And the fix itself goes through a little bit back and forth. So I’d just like to share it since it is an interesting experience.
Between the communication of application and input method server. A common thing is application send a key event to input method and wait for the handling result of this event. Fcitx allows multiple different mode for it, including sync and async mode. By default it is async mode to avoid any block on the application side. But since we also support sync mode, we should keep two implementation have the same effect.
But in reality, we do behave the same under most circumstance, except when you bypass the key event and do some other thing at the same time. The reason behind this is actually easy to understand. Check the pseudo code below.
DBusCall call = inputContext->processKeyEvent();
call.waitForFinished();
DBusCall call = inputContext->processKeyEvent();
call.onFinished(processKeyEventCallback);
Imagine the input method issues commitString request at the same time. In the first sync case, the key event will be handled before the commitString, while the latter one will handle commitString before the key event.
If the key event is filtered, then there is no difference, but if the key is not filtered, then we will get two different result. For example, keyboard engine will show spell check within the preedit, but when you press space, the preedit will be commit to client and also the space key. You may say you can solve it by just combining the space key within the commit string request. Then what about a “left” key that doesn’t produce text? One way to solve it is to piggyback a rich result value instead of just a boolean value, but that would require more code change to existing interface. So in order to resolve this, we will force the input method to use the forward key interface to send the key back. Luckily, all event can be handled by the same way. The trick can be done within the last reserved event handler for key event.
Here’s the things changed with in the fcitx framework.
- Engine can safely assume any commitString is handled before the key event. If engine want it the other way, it can use forward key request itself. This keeps the default behavior for all input context.
- DBus based input context will block all the request sending the client until the request is returned. This ensures processKeyEvent is returned before the following request.
- If there is any pending request, mark processKeyEvent request as true, and use forwardKey to re-send the key event to the client.
- Within the input context code, tries to use the same data to forward key event if possible to avoid any conversion error.