Skip to content

Instantly share code, notes, and snippets.

@garasubo
Created January 25, 2020 05:14
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garasubo/8ebb893f2af1950254df715fcf1de58e to your computer and use it in GitHub Desktop.
Save garasubo/8ebb893f2af1950254df715fcf1de58e to your computer and use it in GitHub Desktop.
Example code to use preedit callbacks in XIM
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <assert.h>
Display *dpy;
Window win;
int scr;
void send_spot(XIC ic, XPoint nspot)
{
XVaNestedList preedit_attr;
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &nspot, NULL);
XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
XFree(preedit_attr);
}
static int preedit_start_callback(
XIM xim,
XPointer client_data,
XPointer call_data)
{
printf("preedit start\n");
// no length limit
return -1;
}
static void preedit_done_callback(
XIM xim,
XPointer client_data,
XPointer call_data)
{
printf("preedit done\n");
}
static void preedit_draw_callback(
XIM xim,
XPointer client_data,
XIMPreeditDrawCallbackStruct *call_data)
{
printf("callback\n");
XIMText *xim_text = call_data->text;
if (xim_text != NULL)
{
printf("Draw callback string: %s, length: %d, first: %d, caret: %d\n", xim_text->string.multi_byte, call_data->chg_length, call_data->chg_first, call_data->caret);
}
else
{
printf("Draw callback string: (DELETED), length: %d, first: %d, caret: %d\n", call_data->chg_length, call_data->chg_first, call_data->caret);
}
}
static void preedit_caret_callback(
XIM xim,
XPointer client_data,
XIMPreeditCaretCallbackStruct *call_data)
{
printf("preedit caret\n");
if (call_data != NULL)
{
printf("direction: %d position: %d\n", call_data->direction, call_data->position);
}
}
int main()
{
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
dpy = XOpenDisplay(NULL);
scr = DefaultScreen(dpy);
win = XCreateSimpleWindow(dpy,
XDefaultRootWindow(dpy),
0, 0, 100, 100, 5,
BlackPixel(dpy, scr),
BlackPixel(dpy, scr));
XMapWindow(dpy, win);
XIMCallback draw_callback;
draw_callback.client_data = NULL;
draw_callback.callback = (XIMProc)preedit_draw_callback;
XIMCallback start_callback;
start_callback.client_data = NULL;
start_callback.callback = (XIMProc)preedit_start_callback;
XIMCallback done_callback;
done_callback.client_data = NULL;
done_callback.callback = (XIMProc)preedit_done_callback;
XIMCallback caret_callback;
caret_callback.client_data = NULL;
caret_callback.callback = (XIMProc)preedit_caret_callback;
XVaNestedList preedit_attributes = XVaCreateNestedList(
0,
XNPreeditStartCallback, &start_callback,
XNPreeditDoneCallback, &done_callback,
XNPreeditDrawCallback, &draw_callback,
XNPreeditCaretCallback, &caret_callback,
NULL);
XIM xim = XOpenIM(dpy, NULL, NULL, NULL);
XIC ic = XCreateIC(xim,
XNInputStyle, XIMPreeditCallbacks | XIMStatusNothing,
XNClientWindow, win,
XNPreeditAttributes, preedit_attributes,
NULL);
XSetICFocus(ic);
XSelectInput(dpy, win, KeyPressMask);
XPoint spot;
spot.x = 0;
spot.y = 0;
send_spot(ic, spot);
static char *buff;
size_t buff_size = 16;
buff = (char *)malloc(buff_size);
for (;;) {
KeySym ksym;
Status status;
XEvent ev;
XNextEvent(dpy, &ev);
if (XFilterEvent(&ev, None)) {
continue;
}
if (ev.type == KeyPress) {
size_t c = Xutf8LookupString(ic, &ev.xkey,
buff, buff_size - 1,
&ksym, &status);
if (status == XBufferOverflow) {
printf("reallocate to the size of: %lu\n", c + 1);
buff = realloc(buff, c + 1);
c = XmbLookupString(ic, &ev.xkey,
buff, c,
&ksym, &status);
}
if (c) {
spot.x += 20;
spot.y += 20;
send_spot(ic, spot);
buff[c] = 0;
printf("delievered string: %s\n", buff);
}
}
}
}
@tuxzz
Copy link

tuxzz commented Dec 30, 2021

The spot location doesn't work in XIMPreeditCallbacks style. Any solution?

@garasubo
Copy link
Author

garasubo commented Jan 6, 2022

@tuxzz What do you mean by "spot location"? You mean caret in call_data?
This snippet is an example code for my blog post, so please check it as well.
https://garasubo.github.io/hexo/2020/01/25/xim.html

@tuxzz
Copy link

tuxzz commented Jan 7, 2022

@garasubo No. I mean the XNSpotLocation value set in send_spot(). It should move the position of ime candidate box. But it doesn't work with XIMPreeditCallbacks. When using XIMPreeditCallbacks, the candidate box always stuck under the bottom of window.

@garasubo
Copy link
Author

garasubo commented Jan 8, 2022

Oh, I see. Well, I didn't care about XNSpotLocation value so much, so I don't have much insight about it. Sorry.

@tuxzz
Copy link

tuxzz commented Jan 9, 2022

@garasubo I discussed with the author of fcitx. Seems there is no easy way to get XNSpotLocation to work with XNPreeditCallbacks. Perhaps I should dismiss this problem since the preedit in IME itself also works...

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