[PATCH 12/19] Xephyr: use xcb for event handling

Eric Anholt eric at anholt.net
Mon Aug 26 13:20:56 PDT 2013


From: Julien Cristau <jcristau at debian.org>

v2: Rebase on indentation changes, squash in a simpler variant of the
    later event compression patch, fix server hang or segfault on
    window close by reimplementing the x_io_error_handler in the new
    XCB event loop (anholt).

Reviewed-by: Mikhail Gusarov <dottedmag at dottedmag.net> (v1)
Signed-off-by: Julien Cristau <jcristau at debian.org>
Signed-off-by: Eric Anholt <eric at anholt.net>
---
 hw/kdrive/ephyr/hostx.c | 284 ++++++++++++++++++++++++++----------------------
 1 file changed, 155 insertions(+), 129 deletions(-)

diff --git a/hw/kdrive/ephyr/hostx.c b/hw/kdrive/ephyr/hostx.c
index 3d3519b..07a2772 100644
--- a/hw/kdrive/ephyr/hostx.c
+++ b/hw/kdrive/ephyr/hostx.c
@@ -942,169 +942,195 @@ host_screen_from_window(Window w)
 int
 hostx_get_event(EphyrHostXEvent * ev)
 {
-    XEvent xev;
+    xcb_generic_event_t *xev;
     static int grabbed_screen = -1;
     static xcb_key_symbols_t *keysyms;
 
     if (!keysyms)
         keysyms = xcb_key_symbols_alloc(HostX.conn);
 
-    if (XPending(HostX.dpy)) {
-        XNextEvent(HostX.dpy, &xev);
-
-        switch (xev.type) {
-        case Expose:
-            /* Not so great event compression, but works ok */
-            while (XCheckTypedWindowEvent(HostX.dpy, xev.xexpose.window,
-                                          Expose, &xev));
-            {
-                struct EphyrHostScreen *host_screen =
-                    host_screen_from_window(xev.xexpose.window);
-                if (host_screen) {
-                    hostx_paint_rect(host_screen->info, 0, 0, 0, 0,
-                                     host_screen->win_width,
-                                     host_screen->win_height);
-                }
-                else {
-                    EPHYR_LOG_ERROR("failed to get host screen\n");
-                    ev->type = EPHYR_EV_EXPOSE;
-                    ev->data.expose.window = xev.xexpose.window;
-                    return 1;
-                }
-            }
-            return 0;
-
-        case MotionNotify:
-        {
-            struct EphyrHostScreen *host_screen =
-                host_screen_from_window(xev.xmotion.window);
-
-            ev->type = EPHYR_EV_MOUSE_MOTION;
-            ev->data.mouse_motion.x = xev.xmotion.x;
-            ev->data.mouse_motion.y = xev.xmotion.y;
-            ev->data.mouse_motion.window = xev.xmotion.window;
-            ev->data.mouse_motion.screen =
-                (host_screen ? host_screen->mynum : -1);
+    xev = xcb_poll_for_event(HostX.conn);
+    if (!xev) {
+        /* If our XCB connection has died (for example, our window was
+         * closed), exit now.
+         */
+        if (xcb_connection_has_error(HostX.conn)) {
+            CloseWellKnownConnections();
+            OsCleanup(1);
+            exit(1);
         }
-            return 1;
 
-        case ButtonPress:
-            ev->type = EPHYR_EV_MOUSE_PRESS;
-            ev->key_state = xev.xkey.state;
-            /* 
-             * This is a bit hacky. will break for button 5 ( defined as 0x10 )
-             * Check KD_BUTTON defines in kdrive.h 
-             */
-            ev->data.mouse_down.button_num = 1 << (xev.xbutton.button - 1);
-            return 1;
+        return 0;
+    }
 
-        case ButtonRelease:
-            ev->type = EPHYR_EV_MOUSE_RELEASE;
-            ev->key_state = xev.xkey.state;
-            ev->data.mouse_up.button_num = 1 << (xev.xbutton.button - 1);
-            return 1;
+    switch (xev->response_type & 0x7f) {
+    case XCB_EXPOSE: {
+        xcb_expose_event_t *expose = (xcb_expose_event_t *)xev;
+        struct EphyrHostScreen *host_screen =
+            host_screen_from_window(expose->window);
 
-        case KeyPress:
-        {
-            ev->type = EPHYR_EV_KEY_PRESS;
-            ev->key_state = xev.xkey.state;
-            ev->data.key_down.scancode = xev.xkey.keycode;
+        /* Wait for the last expose event in a series of cliprects
+         * to actually paint our screen.
+         */
+        if (expose->count != 0)
+            break;
+
+        if (host_screen) {
+            hostx_paint_rect(host_screen->info, 0, 0, 0, 0,
+                             host_screen->win_width,
+                             host_screen->win_height);
+        }
+        else {
+            EPHYR_LOG_ERROR("failed to get host screen\n");
+            ev->type = EPHYR_EV_EXPOSE;
+            ev->data.expose.window = expose->window;
+            free(xev);
             return 1;
         }
-        case KeyRelease:
-            if ((xcb_key_symbols_get_keysym(keysyms,xev.xkey.keycode, 0) == XK_Shift_L
-                 || xcb_key_symbols_get_keysym(keysyms,xev.xkey.keycode, 0) == XK_Shift_R)
-                && (xev.xkey.state & XCB_MOD_MASK_CONTROL)) {
-                struct EphyrHostScreen *host_screen =
-                    host_screen_from_window(xev.xexpose.window);
-
-                if (grabbed_screen != -1) {
-                    xcb_ungrab_keyboard(HostX.conn, XCB_TIME_CURRENT_TIME);
+        return 0;
+    }
+
+    case XCB_MOTION_NOTIFY: {
+        xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)xev;
+        struct EphyrHostScreen *host_screen =
+            host_screen_from_window(motion->event);
+
+        ev->type = EPHYR_EV_MOUSE_MOTION;
+        ev->data.mouse_motion.x = motion->event_x;
+        ev->data.mouse_motion.y = motion->event_y;
+        ev->data.mouse_motion.window = motion->event;
+        ev->data.mouse_motion.screen =
+            (host_screen ? host_screen->mynum : -1);
+        free(xev);
+        return 1;
+    }
+
+    case XCB_BUTTON_PRESS: {
+        xcb_button_press_event_t *button = (xcb_button_press_event_t *)xev;
+        ev->type = EPHYR_EV_MOUSE_PRESS;
+        ev->key_state = button->state;
+        /* 
+         * This is a bit hacky. will break for button 5 ( defined as 0x10 )
+         * Check KD_BUTTON defines in kdrive.h 
+         */
+        ev->data.mouse_down.button_num = 1 << (button->detail - 1);
+        free(xev);
+        return 1;
+    }
+
+    case XCB_BUTTON_RELEASE: {
+        xcb_button_release_event_t *button = (xcb_button_release_event_t *)xev;
+        ev->type = EPHYR_EV_MOUSE_RELEASE;
+        ev->key_state = button->state;
+        ev->data.mouse_up.button_num = 1 << (button->detail-1);
+        free(xev);
+        return 1;
+    }
+
+    case XCB_KEY_PRESS: {
+        xcb_key_press_event_t *key = (xcb_key_press_event_t *)xev;
+        ev->type = EPHYR_EV_KEY_PRESS;
+        ev->key_state = key->state;
+        ev->data.key_down.scancode = key->detail;
+        free(xev);
+        return 1;
+    }
+
+    case XCB_KEY_RELEASE: {
+        xcb_key_release_event_t *key = (xcb_key_release_event_t *)xev;
+        if ((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_L
+             || xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_R)
+            && (key->state & XCB_MOD_MASK_CONTROL)) {
+            struct EphyrHostScreen *host_screen =
+                host_screen_from_window(key->event);
+
+            if (grabbed_screen != -1) {
+                xcb_ungrab_keyboard(HostX.conn, XCB_TIME_CURRENT_TIME);
+                xcb_ungrab_pointer(HostX.conn, XCB_TIME_CURRENT_TIME);
+                grabbed_screen = -1;
+                hostx_set_win_title(host_screen->info,
+                                    "(ctrl+shift grabs mouse and keyboard)");
+            }
+            else {
+                /* Attempt grab */
+                xcb_grab_keyboard_cookie_t kbgrabc =
+                    xcb_grab_keyboard(HostX.conn,
+                                      True,
+                                      host_screen->win,
+                                      XCB_TIME_CURRENT_TIME,
+                                      XCB_GRAB_MODE_ASYNC,
+                                      XCB_GRAB_MODE_ASYNC);
+                xcb_grab_keyboard_reply_t *kbgrabr;
+                xcb_grab_pointer_cookie_t pgrabc =
+                    xcb_grab_pointer(HostX.conn,
+                                     True,
+                                     host_screen->win,
+                                     0,
+                                     XCB_GRAB_MODE_ASYNC,
+                                     XCB_GRAB_MODE_ASYNC,
+                                     host_screen->win,
+                                     XCB_NONE,
+                                     XCB_TIME_CURRENT_TIME);
+                xcb_grab_pointer_reply_t *pgrabr;
+                kbgrabr = xcb_grab_keyboard_reply(HostX.conn, kbgrabc, NULL);
+                if (!kbgrabr || kbgrabr->status != XCB_GRAB_STATUS_SUCCESS) {
+                    xcb_discard_reply(HostX.conn, pgrabc.sequence);
                     xcb_ungrab_pointer(HostX.conn, XCB_TIME_CURRENT_TIME);
-                    grabbed_screen = -1;
-                    hostx_set_win_title(host_screen->info,
-                                        "(ctrl+shift grabs mouse and keyboard)");
-                }
-                else {
-                    /* Attempt grab */
-                    xcb_grab_keyboard_cookie_t kbgrabc =
-                        xcb_grab_keyboard(HostX.conn,
-                                          True,
-                                          host_screen->win,
-                                          XCB_TIME_CURRENT_TIME,
-                                          XCB_GRAB_MODE_ASYNC,
-                                          XCB_GRAB_MODE_ASYNC);
-                    xcb_grab_keyboard_reply_t *kbgrabr;
-                    xcb_grab_pointer_cookie_t pgrabc =
-                        xcb_grab_pointer(HostX.conn,
-                                         True,
-                                         host_screen->win,
-                                         0,
-                                         XCB_GRAB_MODE_ASYNC,
-                                         XCB_GRAB_MODE_ASYNC,
-                                         host_screen->win,
-                                         XCB_NONE,
-                                         XCB_TIME_CURRENT_TIME);
-                    xcb_grab_pointer_reply_t *pgrabr;
-                    kbgrabr = xcb_grab_keyboard_reply(HostX.conn, kbgrabc, NULL);
-                    if (!kbgrabr || kbgrabr->status != XCB_GRAB_STATUS_SUCCESS) {
-                        xcb_discard_reply(HostX.conn, pgrabc.sequence);
-                        xcb_ungrab_pointer(HostX.conn, XCB_TIME_CURRENT_TIME);
-                    } else {
-                        pgrabr = xcb_grab_pointer_reply(HostX.conn, pgrabc, NULL);
-                        if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS)
-                            {
-                                xcb_ungrab_keyboard(HostX.conn,
-                                                    XCB_TIME_CURRENT_TIME);
-                            } else {
-                            grabbed_screen = host_screen->mynum;
-                            hostx_set_win_title
-                                (host_screen->info,
-                                 "(ctrl+shift releases mouse and keyboard)");
-                        }
+                } else {
+                    pgrabr = xcb_grab_pointer_reply(HostX.conn, pgrabc, NULL);
+                    if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS)
+                        {
+                            xcb_ungrab_keyboard(HostX.conn,
+                                                XCB_TIME_CURRENT_TIME);
+                        } else {
+                        grabbed_screen = host_screen->mynum;
+                        hostx_set_win_title
+                            (host_screen->info,
+                             "(ctrl+shift releases mouse and keyboard)");
                     }
                 }
             }
+        }
 
-            /* Still send the release event even if above has happened
-             * server will get confused with just an up event. 
-             * Maybe it would be better to just block shift+ctrls getting to
-             * kdrive all togeather. 
-             */
-            ev->type = EPHYR_EV_KEY_RELEASE;
-            ev->key_state = xev.xkey.state;
-            ev->data.key_up.scancode = xev.xkey.keycode;
-            return 1;
+        /* Still send the release event even if above has happened
+         * server will get confused with just an up event. 
+         * Maybe it would be better to just block shift+ctrls getting to
+         * kdrive all togeather. 
+         */
+        ev->type = EPHYR_EV_KEY_RELEASE;
+        ev->key_state = key->state;
+        ev->data.key_up.scancode = key->detail;
+        return 1;
+    }
 
-        case ConfigureNotify:
+    case ConfigureNotify:
         {
             struct EphyrHostScreen *host_screen;
+            xcb_configure_notify_event_t *configure =
+                (xcb_configure_notify_event_t *)xev;
 
-            /* event compression as for Expose events, cause
-             * we don't want to resize the framebuffer for
-             * every single change */
-            while (XCheckTypedWindowEvent(HostX.dpy, xev.xconfigure.window,
-                                          ConfigureNotify, &xev));
-            host_screen = host_screen_from_window(xev.xconfigure.window);
+            host_screen = host_screen_from_window(configure->window);
 
             if (!host_screen ||
                 (host_screen->win_pre_existing == None && !EphyrWantResize)) {
+                free(xev);
                 return 0;
             }
 
             ev->type = EPHYR_EV_CONFIGURE;
-            ev->data.configure.width = xev.xconfigure.width;
-            ev->data.configure.height = xev.xconfigure.height;
-            ev->data.configure.window = xev.xconfigure.window;
+            ev->data.configure.width = configure->width;
+            ev->data.configure.height = configure->height;
+            ev->data.configure.window = configure->window;
             ev->data.configure.screen = host_screen->mynum;
+            free(xev);
 
             return 1;
         }
-        default:
-            break;
+    default:
+        break;
 
-        }
     }
+    free(xev);
     return 0;
 }
 
-- 
1.8.4.rc3



More information about the xorg-devel mailing list