【问题标题】:Get UTF-8 input with X11 Display使用 X11 Display 获取 UTF-8 输入
【发布时间】:2013-08-15 05:31:24
【问题描述】:

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

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

我还编写了一个简单的示例(如下)来尝试使其工作。我的简单测试用例是打印一个“é”,这是通过输入锐音然后是 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);
    }
}

【问题讨论】:

    标签: linux utf-8 x11 xlib


    【解决方案1】:

    你必须这样做:

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

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

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

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

    最后,如 cmets 中所述,对于最新版本的 X11,您需要指定对 XSetLocaleModifiers 的修改,例如 XSetLocaleModifiers("@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);
        }
    }
    

    【讨论】:

    • 我已经弄清楚了这一点,就像您在 2 天前所说的那样,您还指出了一些对我的用例很重要的其他事情。这证实了我的想法是正确的:)。非常感谢您的回答
    • 你是把这个放在XNextEvent()之后还是case KeyPress之后?
    • @exebook 在 XNextEvent 之后我认为是正确的地方。输入法可以将任何类型的事件转换为按键。
    • 我相信示例代码中还有另一个小错误:case KeymapNotify: 应该是case MappingNotify:。如果是这样,那么应该从XSelectInput() 中删除KeymapStateMask
    • 对不起 Necro,但为什么在按键时使用 XLookupString 而在按键时使用 Xutf8LookupString?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多