There are basically two ways:
1) On the kernel level, find the /dev/input device that produces your keypresses, open it and do a "grab"-ioctl (same as evtest --grab
does). That will cause this input device to send the key events exclusively to your application. Then use /dev/uinput
to create your own input device from your application, where you can send key events out. X should connect to that device automatically.
2) On the X level, intercept keypress events just like the window manager does, and send out your own events with XSendEvent
instead. I am not sure a grab would be the best way to do it; grabs are intended for a situation when some application temporarily wants to intercept all events during a specific interaction.
I have no idea what ibus does (maybe even a third method), I haven't looked at it in detail.
Edit
Had to look this up, because it's too long that I read about all the X details.
There are two basic grab functions: XGrabKeyboard
, which generates FocusIn and FocusOut events, and takes complete control of the keyboard (active grab). This is the function I meant when talking about X grabs above, and this is the function that should only be active temporarily.
There is also XGrabKey
, which registers a passive grab for specific keycodes. Looking very quickly at the source code of the window manager fvwm
, this seems to be what the window manager uses.
The details of this are complicated enough that you probably want to dig up some documentation about how to program a window manager (or read source code, maybe even ibus
source code).