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.
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).