7

我一直在尝试并阅读互联网上的大量资源,试图找到一种从 X 显示器获取 UTF-8 键盘(组合)输入的方法。但我无法让它工作。

我已经尝试了此链接中的示例代码(例如 11-4),但没有成功。

我还编写了一个简单的示例(如下)来尝试使其工作。我的简单测试用例是打印一个“é”,它是通过输入acute 然后是e 来实现的。

怎么了?

谢谢,

这是我的例子:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xlocale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char ** argv)
{
    int screen_num, width, height;
    unsigned long background, border;
    Window win;
    XEvent ev;
    Display *dpy;
    XIM im;
    XIC ic;
    char *failed_arg;
    XIMStyles *styles;
    XIMStyle xim_requested_style;

    /* First connect to the display server, as specified in the DISPLAY 
    environment variable. */
    if (setlocale(LC_ALL, "") == NULL) {
        return 9;
    }

    if (!XSupportsLocale()) {
        return 10;
    }
    if (XSetLocaleModifiers("") == NULL) {
        return 11;
    }

    dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "unable to connect to display");
        return 7;
    }
    /* these are macros that pull useful data out of the display object */
    /* we use these bits of info enough to want them in their own variables */
    screen_num = DefaultScreen(dpy);
    background = BlackPixel(dpy, screen_num);
    border = WhitePixel(dpy, screen_num);

    width = 400; /* start with a small window */
    height = 200;

    win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), /* display, parent */
        0,0, /* x, y: the window manager will place the window elsewhere */
        width, height, /* width, height */
        2, border, /* border width & colour, unless you have a window manager */
        background); /* background colour */

    /* tell the display server what kind of events we would like to see */
    XSelectInput(dpy, win, ButtonPressMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|KeymapStateMask);

    /* okay, put the window on the screen, please */
    XMapWindow(dpy, win);

    im = XOpenIM(dpy, NULL, NULL, NULL);
    if (im == NULL) {
        fputs("Could not open input method\n", stdout);
        return 2;
    }

    failed_arg = XGetIMValues(im, XNQueryInputStyle, &styles, NULL);

    if (failed_arg != NULL) {
      fputs("XIM Can't get styles\n", stdout);
      return 3;
    }

    int i;
    for (i = 0; i < styles->count_styles; i++) {
        printf("style %d\n", styles->supported_styles[i]);
    }
    ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
    if (ic == NULL) {
        printf("Could not open IC\n");
        return 4;
    }

    XSetICFocus(ic);

    /* as each event that we asked about occurs, we respond.  In this
     * case we note if the window's shape changed, and exit if a button
     * is pressed inside the window */
    while(1) {
        XNextEvent(dpy, &ev);
        switch(ev.type){
        case KeymapNotify:
            XRefreshKeyboardMapping(&ev.xmapping);
            break;
        case KeyPress:
            {
                int count = 0;
                KeySym keysym = 0;
                char buf[20];
                Status status = 0;
                count = Xutf8LookupString(ic, (XKeyPressedEvent*)&ev, buf, 20, &keysym, &status);

                printf("count: %d\n", count);
                if (status==XBufferOverflow)
                    printf("BufferOverflow\n");

                if (count)
                    printf("buffer: %s\n", buf);

                if (status == XLookupKeySym || status == XLookupBoth) {
                    printf("status: %d\n", status);
                }
                printf("pressed KEY: %d\n", keysym);
            }
            break;
        case KeyRelease:
            {
                int count = 0;
                KeySym keysym = 0;
                char buf[20];
                Status status = 0;
                count = XLookupString((XKeyEvent*)&ev, buf, 20, &keysym, NULL);

                if (count)
                    printf("in release buffer: %s\n", buf);

                printf("released KEY: %d\n", keysym);
            }
            break;
        case ConfigureNotify:
            if (width != ev.xconfigure.width
                    || height != ev.xconfigure.height) {
                width = ev.xconfigure.width;
                height = ev.xconfigure.height;
                printf("Size changed to: %d by %d", width, height);
            }
            break;
        case ButtonPress:
            XCloseDisplay(dpy);
            return 0;
        }
        fflush(stdout);
    }
}
4

1 回答 1

11

你必须这样做:

            if (XFilterEvent(&ev, win))
                continue;

在您的事件循环中。这会运行输入法机制,没有它你会得到原始的 X 事件。例如,当您按下一个死重音键后跟一个字母键,并且不调用XFilterEvent时,您将照常获得两个 KeyPress 事件。但是如果你打电话,你会得到三个事件。有两个原始事件,它们XFilterEvent(&ev, win)返回True。然后输入法合成了一个事件,XFilterEvent(&ev, win)返回False。正是这第三个事件包含重音字符。

如果你想要原始事件和输入法合成的事件,你当然可以做自己的原始事件处理,而不是continue.

请注意buf[count] = 0;,为了buf正确打印(或显式使用长度),您需要Xutf8LookupString不以空值终止其输出。

最后,正如评论中提到的,对于 X11 的最新版本,您需要指定修改XSetLocaleModifiersXSetLocaleModifiers("@im=none"),否则不会生成额外的事件。

这是代码的更正版本:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xlocale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char ** argv)
{
    int screen_num, width, height;
    unsigned long background, border;
    Window win;
    XEvent ev;
    Display *dpy;
    XIM im;
    XIC ic;
    char *failed_arg;
    XIMStyles *styles;
    XIMStyle xim_requested_style;

    /* First connect to the display server, as specified in the DISPLAY 
    environment variable. */
    if (setlocale(LC_ALL, "") == NULL) {
        return 9;
    }

    if (!XSupportsLocale()) {
        return 10;
    }
    if (XSetLocaleModifiers("@im=none") == NULL) {
        return 11;
    }

    dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "unable to connect to display");
        return 7;
    }
    /* these are macros that pull useful data out of the display object */
    /* we use these bits of info enough to want them in their own variables */
    screen_num = DefaultScreen(dpy);
    background = BlackPixel(dpy, screen_num);
    border = WhitePixel(dpy, screen_num);

    width = 400; /* start with a small window */
    height = 200;

    win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), /* display, parent */
        0,0, /* x, y: the window manager will place the window elsewhere */
        width, height, /* width, height */
        2, border, /* border width & colour, unless you have a window manager */
        background); /* background colour */

    /* tell the display server what kind of events we would like to see */
    XSelectInput(dpy, win, ButtonPressMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask);

    /* okay, put the window on the screen, please */
    XMapWindow(dpy, win);

    im = XOpenIM(dpy, NULL, NULL, NULL);
    if (im == NULL) {
        fputs("Could not open input method\n", stdout);
        return 2;
    }

    failed_arg = XGetIMValues(im, XNQueryInputStyle, &styles, NULL);

    if (failed_arg != NULL) {
      fputs("XIM Can't get styles\n", stdout);
      return 3;
    }

    int i;
    for (i = 0; i < styles->count_styles; i++) {
        printf("style %d\n", (int)styles->supported_styles[i]);
    }
    ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
    if (ic == NULL) {
        printf("Could not open IC\n");
        return 4;
    }

    XSetICFocus(ic);

    /* as each event that we asked about occurs, we respond.  In this
     * case we note if the window's shape changed, and exit if a button
     * is pressed inside the window */
    while(1) {
        XNextEvent(dpy, &ev);
        if (XFilterEvent(&ev, win))
            continue;
        switch(ev.type){
        case MappingNotify:
            XRefreshKeyboardMapping(&ev.xmapping);
            break;
        case KeyPress:
            {
                int count = 0;
                KeySym keysym = 0;
                char buf[20];
                Status status = 0;
                count = Xutf8LookupString(ic, (XKeyPressedEvent*)&ev, buf, 20, &keysym, &status);

                printf("count: %d\n", count);
                if (status==XBufferOverflow)
                    printf("BufferOverflow\n");

                if (count)
                    printf("buffer: %.*s\n", count, buf);

                if (status == XLookupKeySym || status == XLookupBoth) {
                    printf("status: %d\n", status);
                }
                printf("pressed KEY: %d\n", (int)keysym);
            }
            break;
        case KeyRelease:
            {
                int count = 0;
                KeySym keysym = 0;
                char buf[20];
                Status status = 0;
                count = XLookupString((XKeyEvent*)&ev, buf, 20, &keysym, NULL);

                if (count)
                    printf("in release buffer: %.*s\n", count, buf);

                printf("released KEY: %d\n", (int)keysym);
            }
            break;
        case ConfigureNotify:
            if (width != ev.xconfigure.width
                    || height != ev.xconfigure.height) {
                width = ev.xconfigure.width;
                height = ev.xconfigure.height;
                printf("Size changed to: %d by %d", width, height);
            }
            break;
        case ButtonPress:
            XCloseDisplay(dpy);
            return 0;
        }
        fflush(stdout);
    }
}
于 2013-08-17T11:34:15.427 回答