2013年1月7日 星期一

XIM client 範例程式

這個程式示範如何用最少的程式碼在 X window 程式中接收 XIM 輸入法的輸入字串。Input style 為 Over the spot。
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xlocale.h>

#define TITLE_STRING "XIM test"

int main(int argc, char **argv)
{
	int quit = False;
	Display* display = NULL;
	Window window;
	XTextProperty  title;
	XEvent event;
	Atom wm_delete_window;

	XIM xim;
	XIC xic;
	int xim_status;
	KeySym keysym;
	int count = 0;
	unsigned long fevent;
	char buffer[256]= {};

	/* Call XSetLocaleModifiers before initialize XIM */

	setlocale(LC_ALL, "");
	XSetLocaleModifiers("");

	display = XOpenDisplay(NULL);
	window = XCreateWindow(display, RootWindow(display, 0), 0, 0, 512, 512, 0,  CopyFromParent, CopyFromParent,  CopyFromParent, 0, NULL);

	XSetWindowBackground(display, window, BlackPixel(display, 0));

	title.value = (unsigned char*)TITLE_STRING;
	title.encoding = XA_STRING;
	title.format = 8;
	title.nitems = strlen(TITLE_STRING);

	XSetWMProperties(display, window, &title, &title, NULL, 0, NULL, NULL, NULL);

	wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
	XSetWMProtocols(display, window, &wm_delete_window, 1);

	XMapWindow(display, window);


	/* XIM initialization */

	xim = XOpenIM(display, NULL, NULL, NULL);
	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing ,XNClientWindow, window, NULL);


	/* We need IC event to switch IME */

	XGetICValues(xic, XNFilterEvents, &fevent, NULL);

	XSelectInput(display, window, ExposureMask | KeyPressMask | FocusChangeMask | StructureNotifyMask | fevent);

	XSetICFocus(xic);

	while(!quit)
	{
		XNextEvent(display, &event);
		if(XFilterEvent(&event, None))
		        continue;

		switch(event.type)
		{
		case KeyPress:
			count = XmbLookupString(xic, &event.xkey, buffer, 256, &keysym, &xim_status);

			/* Cut redundant chars */
			buffer[count] = 0;

			if(xim_status == XLookupChars || xim_status == XLookupBoth)
				fprintf(stderr, "%s\n", buffer);

			break;
		case ClientMessage:
			if(event.xclient.data.l[0] == wm_delete_window)
			{
				XDestroyWindow(display, window);
				quit = True;
			}
			break;
		case FocusIn:
			XSetICFocus(xic);
			break;

		case FocusOut:
			XUnsetICFocus(xic);
			break;
		}
	}

	XCloseDisplay(display);
	return 0;
}