[RFC,kdrive/ephyr,v2] Match host X server's keymap

Submitted by Laércio de Sousa on Dec. 8, 2015, 12:46 p.m.

Details

Message ID 1449578808-14541-1-git-send-email-laerciosousa@sme-mogidascruzes.sp.gov.br
State Superseded
Headers show
Series "Match host X server's keymap" ( rev: 2 ) in X.org (DEPRECATED - USE GITLAB)

Not browsing as part of any series.

Commit Message

Laércio de Sousa Dec. 8, 2015, 12:46 p.m.
Analogous to Xnest implementation at 83fef4235db86343477b4ec9858c6ba35e1aa7d9.

v2 changes:
  - Keep struct KdKeyboardInfo untouched.
  - Move XkbApplyMappingChange()+XkbDDXChangeControls() call to
    EphyrKeyboardInit(), since it doesn't apply for e.g. kdrive evdev driver.
  - Call kdrive keyboard driver's Init() after InitKeyboardDeviceStruct(),
    so we can apply keymap change within EphyrKeyboardInit(), as described
    above.

Signed-off-by: Laércio de Sousa <laerciosousa@sme-mogidascruzes.sp.gov.br>
---
 configure.ac            |  2 +-
 hw/kdrive/ephyr/ephyr.c | 30 ++++++++++++---
 hw/kdrive/ephyr/hostx.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++---
 hw/kdrive/ephyr/hostx.h |  9 +----
 hw/kdrive/src/kinput.c  |  8 ++--
 5 files changed, 123 insertions(+), 24 deletions(-)

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index 2e38efa..933addc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2391,7 +2391,7 @@  if test "$KDRIVE" = yes; then
        AC_DEFINE(KDRIVE_MOUSE, 1, [Enable KDrive mouse driver])
     fi
 
-    XEPHYR_REQUIRED_LIBS="xau xdmcp xcb xcb-shape xcb-render xcb-renderutil xcb-aux xcb-image xcb-icccm xcb-shm xcb-keysyms xcb-randr"
+    XEPHYR_REQUIRED_LIBS="xau xdmcp xcb xcb-shape xcb-render xcb-renderutil xcb-aux xcb-image xcb-icccm xcb-shm xcb-keysyms xcb-randr xcb-xkb"
     if test "x$XV" = xyes; then
         XEPHYR_REQUIRED_LIBS="$XEPHYR_REQUIRED_LIBS xcb-xv"
     fi
diff --git a/hw/kdrive/ephyr/ephyr.c b/hw/kdrive/ephyr/ephyr.c
index 896bac5..a477ecc 100644
--- a/hw/kdrive/ephyr/ephyr.c
+++ b/hw/kdrive/ephyr/ephyr.c
@@ -54,7 +54,6 @@  extern Bool ephyr_glamor;
 
 KdKeyboardInfo *ephyrKbd;
 KdPointerInfo *ephyrMouse;
-EphyrKeySyms ephyrKeySyms;
 Bool ephyrNoDRI = FALSE;
 Bool ephyrNoXV = FALSE;
 
@@ -1367,16 +1366,35 @@  KdPointerDriver EphyrMouseDriver = {
 static Status
 EphyrKeyboardInit(KdKeyboardInfo * ki)
 {
+    int i;
+    KeySymsRec keySyms;
+    CARD8 modmap[MAP_LENGTH];
+    XkbControlsRec controls;
+
     ki->driverPrivate = (EphyrKbdPrivate *)
         calloc(sizeof(EphyrKbdPrivate), 1);
-    hostx_load_keymap();
-    if (!ephyrKeySyms.minKeyCode) {
+
+    if (hostx_load_keymap(&keySyms, modmap, &controls) && keySyms.map != NULL) {
+        XkbApplyMappingChange(ki->dixdev, &keySyms,
+                              keySyms.minKeyCode,
+                              keySyms.maxKeyCode - keySyms.minKeyCode + 1,
+                              modmap, serverClient);
+        XkbDDXChangeControls(ki->dixdev, &controls, &controls);
+        free(keySyms.map);
+    }
+
+    if (!keySyms.minKeyCode) {
         ErrorF("Couldn't load keymap from host\n");
         return BadAlloc;
     }
-    ki->minScanCode = ephyrKeySyms.minKeyCode;
-    ki->maxScanCode = ephyrKeySyms.maxKeyCode;
-    free(ki->name);
+
+    ki->minScanCode = keySyms.minKeyCode;
+    ki->maxScanCode = keySyms.maxKeyCode;
+
+    if (ki->name != NULL) {
+        free(ki->name);
+    }
+
     ki->name = strdup("Xephyr virtual keyboard");
     ephyrKbd = ki;
     return Success;
diff --git a/hw/kdrive/ephyr/hostx.c b/hw/kdrive/ephyr/hostx.c
index 49516bb..249b210 100644
--- a/hw/kdrive/ephyr/hostx.c
+++ b/hw/kdrive/ephyr/hostx.c
@@ -52,6 +52,7 @@ 
 #include <xcb/shape.h>
 #include <xcb/xcb_keysyms.h>
 #include <xcb/randr.h>
+#include <xcb/xkb.h>
 #ifdef XF86DRI
 #include <xcb/xf86dri.h>
 #include <xcb/glx.h>
@@ -90,8 +91,6 @@  static EphyrHostXVars HostX;
 
 static int HostXWantDamageDebug = 0;
 
-extern EphyrKeySyms ephyrKeySyms;
-
 extern Bool EphyrWantResize;
 
 char *ephyrResName = NULL;
@@ -1086,18 +1085,105 @@  hostx_paint_debug_rect(KdScreenInfo *screen,
     nanosleep(&tspec, NULL);
 }
 
-void
-hostx_load_keymap(void)
+Bool
+hostx_load_keymap(KeySymsPtr keySyms, CARD8 *modmap, XkbControlsPtr controls)
 {
     int min_keycode, max_keycode;
+    int map_width;
+    size_t i, j;
+    int keymap_len;
+    xcb_keysym_t *keymap;
+    xcb_keycode_t *modifier_map;
+    xcb_get_keyboard_mapping_cookie_t mapping_c;
+    xcb_get_keyboard_mapping_reply_t *mapping_r;
+    xcb_get_modifier_mapping_cookie_t modifier_c;
+    xcb_get_modifier_mapping_reply_t *modifier_r;
+    xcb_xkb_use_extension_cookie_t use_c;
+    xcb_xkb_use_extension_reply_t *use_r;
+    xcb_xkb_get_controls_cookie_t controls_c;
+    xcb_xkb_get_controls_reply_t *controls_r;
 
     min_keycode = xcb_get_setup(HostX.conn)->min_keycode;
     max_keycode = xcb_get_setup(HostX.conn)->max_keycode;
 
     EPHYR_DBG("min: %d, max: %d", min_keycode, max_keycode);
 
-    ephyrKeySyms.minKeyCode = min_keycode;
-    ephyrKeySyms.maxKeyCode = max_keycode;
+    keySyms->minKeyCode = min_keycode;
+    keySyms->maxKeyCode = max_keycode;
+
+    use_c = xcb_xkb_use_extension(HostX.conn,
+                                  XCB_XKB_MAJOR_VERSION,
+                                  XCB_XKB_MINOR_VERSION);
+    use_r = xcb_xkb_use_extension_reply(HostX.conn, use_c, NULL);
+
+    if (!use_r) {
+        EPHYR_LOG_ERROR("Couldn't use XKB extension.");
+        return FALSE;
+    } else if (!use_r->supported) {
+        EPHYR_LOG_ERROR("XKB extension is not supported in X server.");
+        free(use_r);
+        return FALSE;
+    }
+
+    free(use_r);
+
+    controls_c = xcb_xkb_get_controls(HostX.conn,
+                                      XCB_XKB_ID_USE_CORE_KBD);
+    controls_r = xcb_xkb_get_controls_reply(HostX.conn,
+                                            controls_c,
+                                            NULL);
+
+    if (!controls_r) {
+        EPHYR_LOG_ERROR("Couldn't get XKB keyboard controls.");
+        return FALSE;
+    }
+
+    mapping_c = xcb_get_keyboard_mapping(HostX.conn,
+                                         min_keycode,
+                                         max_keycode - min_keycode + 1);
+    mapping_r = xcb_get_keyboard_mapping_reply(HostX.conn,
+                                               mapping_c,
+                                               NULL);
+    map_width = mapping_r->keysyms_per_keycode;
+    keymap = xcb_get_keyboard_mapping_keysyms(mapping_r);
+    keymap_len = xcb_get_keyboard_mapping_keysyms_length(mapping_r);
+
+    modifier_c = xcb_get_modifier_mapping(HostX.conn);
+    modifier_r = xcb_get_modifier_mapping_reply(HostX.conn,
+                                                modifier_c,
+                                                NULL);
+    modifier_map = xcb_get_modifier_mapping_keycodes(modifier_r);
+    memset(modmap, 0, sizeof(CARD8) * MAP_LENGTH);
+
+    for (j = 0; j < 8; j++) {
+        for (i = 0; i < modifier_r->keycodes_per_modifier; i++) {
+            CARD8 keycode;
+
+            if ((keycode = modifier_map[j * modifier_r->keycodes_per_modifier + i])) {
+                modmap[keycode] |= 1 << j;
+            }
+        }
+    }
+
+    free(modifier_r);
+
+    keySyms->mapWidth = map_width;
+    keySyms->map = calloc(keymap_len, sizeof(KeySym));
+
+    for (i = 0; i < keymap_len; i++) {
+        keySyms->map[i] = keymap[i];
+    }
+
+    free(mapping_r);
+
+    controls->enabled_ctrls = controls_r->enabledControls;
+
+    for (i = 0; i < XkbPerKeyBitArraySize; i++) {
+        controls->per_key_repeat[i] = controls_r->perKeyRepeat[i];
+    }
+
+    free(controls_r);
+    return TRUE;
 }
 
 xcb_connection_t *
diff --git a/hw/kdrive/ephyr/hostx.h b/hw/kdrive/ephyr/hostx.h
index d416dae..c907274 100644
--- a/hw/kdrive/ephyr/hostx.h
+++ b/hw/kdrive/ephyr/hostx.h
@@ -44,11 +44,6 @@ 
 typedef struct EphyrHostXVars EphyrHostXVars;
 
 typedef struct {
-    int minKeyCode;
-    int maxKeyCode;
-} EphyrKeySyms;
-
-typedef struct {
     VisualID visualid;
     int screen;
     int depth;
@@ -153,8 +148,8 @@  void
 hostx_paint_rect(KdScreenInfo *screen,
                  int sx, int sy, int dx, int dy, int width, int height);
 
-void
- hostx_load_keymap(void);
+Bool
+hostx_load_keymap(KeySymsPtr keySyms, CARD8 *modmap, XkbControlsPtr controls);
 
 xcb_connection_t *
 hostx_get_xcbconn(void);
diff --git a/hw/kdrive/src/kinput.c b/hw/kdrive/src/kinput.c
index 1fdaa52..d46c2e1 100644
--- a/hw/kdrive/src/kinput.c
+++ b/hw/kdrive/src/kinput.c
@@ -742,10 +742,6 @@  KdKeyboardProc(DeviceIntPtr pDevice, int onoff)
             return BadImplementation;
         }
 
-        if ((*ki->driver->Init) (ki) != Success) {
-            return !Success;
-        }
-
         memset(&rmlvo, 0, sizeof(rmlvo));
         rmlvo.rules = ki->xkbRules;
         rmlvo.model = ki->xkbModel;
@@ -758,6 +754,10 @@  KdKeyboardProc(DeviceIntPtr pDevice, int onoff)
             return BadImplementation;
         }
 
+        if ((*ki->driver->Init) (ki) != Success) {
+            return !Success;
+        }
+
         xiclass = AtomFromName(XI_KEYBOARD);
         AssignTypeAndName(pDevice, xiclass,
                           ki->name ? ki->name : "Generic KDrive Keyboard");

Comments

Hi,

Am 08.12.2015 um 13:46 schrieb Laércio de Sousa:
[...]
> diff --git a/hw/kdrive/ephyr/hostx.c b/hw/kdrive/ephyr/hostx.c
> index 49516bb..249b210 100644
> --- a/hw/kdrive/ephyr/hostx.c
> +++ b/hw/kdrive/ephyr/hostx.c
[...]
> @@ -1086,18 +1085,105 @@ hostx_paint_debug_rect(KdScreenInfo *screen,
>      nanosleep(&tspec, NULL);
>  }
>  
> -void
> -hostx_load_keymap(void)
> +Bool
> +hostx_load_keymap(KeySymsPtr keySyms, CARD8 *modmap, XkbControlsPtr controls)
>  {
>      int min_keycode, max_keycode;
> +    int map_width;
> +    size_t i, j;
> +    int keymap_len;
> +    xcb_keysym_t *keymap;
> +    xcb_keycode_t *modifier_map;
> +    xcb_get_keyboard_mapping_cookie_t mapping_c;
> +    xcb_get_keyboard_mapping_reply_t *mapping_r;
> +    xcb_get_modifier_mapping_cookie_t modifier_c;
> +    xcb_get_modifier_mapping_reply_t *modifier_r;
> +    xcb_xkb_use_extension_cookie_t use_c;
> +    xcb_xkb_use_extension_reply_t *use_r;
> +    xcb_xkb_get_controls_cookie_t controls_c;
> +    xcb_xkb_get_controls_reply_t *controls_r;
>  
>      min_keycode = xcb_get_setup(HostX.conn)->min_keycode;
>      max_keycode = xcb_get_setup(HostX.conn)->max_keycode;
>  
>      EPHYR_DBG("min: %d, max: %d", min_keycode, max_keycode);
>  
> -    ephyrKeySyms.minKeyCode = min_keycode;
> -    ephyrKeySyms.maxKeyCode = max_keycode;
> +    keySyms->minKeyCode = min_keycode;
> +    keySyms->maxKeyCode = max_keycode;
> +
> +    use_c = xcb_xkb_use_extension(HostX.conn,
> +                                  XCB_XKB_MAJOR_VERSION,
> +                                  XCB_XKB_MINOR_VERSION);
> +    use_r = xcb_xkb_use_extension_reply(HostX.conn, use_c, NULL);

Could you check xcb_get_extension_data(HostX.conn, &xcb_xkb_id)->present first?
I don't know why XKB's UseExtension has a supported field, but without this
suggested check, the XCB connection will just go in an error state if the XKB
connection is not actually present.

> +
> +    if (!use_r) {
> +        EPHYR_LOG_ERROR("Couldn't use XKB extension.");
> +        return FALSE;
> +    } else if (!use_r->supported) {
> +        EPHYR_LOG_ERROR("XKB extension is not supported in X server.");
> +        free(use_r);
> +        return FALSE;
> +    }
> +
> +    free(use_r);
> +
> +    controls_c = xcb_xkb_get_controls(HostX.conn,
> +                                      XCB_XKB_ID_USE_CORE_KBD);
> +    controls_r = xcb_xkb_get_controls_reply(HostX.conn,
> +                                            controls_c,
> +                                            NULL);
> +
> +    if (!controls_r) {
> +        EPHYR_LOG_ERROR("Couldn't get XKB keyboard controls.");
> +        return FALSE;

For this one you check for errors, with the following ones you do not. Is there
a reason for that?

> +    }
> +
> +    mapping_c = xcb_get_keyboard_mapping(HostX.conn,
> +                                         min_keycode,
> +                                         max_keycode - min_keycode + 1);
> +    mapping_r = xcb_get_keyboard_mapping_reply(HostX.conn,
> +                                               mapping_c,
> +                                               NULL);
> +    map_width = mapping_r->keysyms_per_keycode;
> +    keymap = xcb_get_keyboard_mapping_keysyms(mapping_r);
> +    keymap_len = xcb_get_keyboard_mapping_keysyms_length(mapping_r);
> +
> +    modifier_c = xcb_get_modifier_mapping(HostX.conn);
> +    modifier_r = xcb_get_modifier_mapping_reply(HostX.conn,
> +                                                modifier_c,
> +                                                NULL);

Also, even though it will never matter much, could you first send all the
requests and then get the replies?

[...]

No clue about the rest. I don't really understand this. :-)

Cheers,
Uli
2015-12-08 16:55 GMT-02:00 Uli Schlachter <psychon@znc.in>:

> Could you check xcb_get_extension_data(HostX.conn, &xcb_xkb_id)->present
> first?
> I don't know why XKB's UseExtension has a supported field, but without this
> suggested check, the XCB connection will just go in an error state if the
> XKB
> connection is not actually present.
>

Done here. Thanks!


> > +    if (!controls_r) {
> > +        EPHYR_LOG_ERROR("Couldn't get XKB keyboard controls.");
> > +        return FALSE;
>
> For this one you check for errors, with the following ones you do not. Is
> there
> a reason for that?
>

Sincerely, there's not. I'm including the other checks as well. Thanks!


> Also, even though it will never matter much, could you first send all the
> requests and then get the replies?
>

I've reorganized the code to send all requests at beginning and process
the replies as they are needed. I'm submitting a v3 right now.

Thank you very much!