Bug 1470047 - Implement InitBySystemSettingsWayland() and get key modifiers on Wayland, r?ashie draft
authorMartin Stransky <stransky@redhat.com>
Tue, 10 Jul 2018 18:28:32 +0200
changeset 817326 c1a9891d2d181803dd8885ebcad1b6360f54f77d
parent 817312 fe17acc6e291e54463db3ea82697c714ae5a4b27
push id116018
push userstransky@redhat.com
push dateThu, 12 Jul 2018 11:20:05 +0000
reviewersashie
bugs1470047
milestone63.0a1
Bug 1470047 - Implement InitBySystemSettingsWayland() and get key modifiers on Wayland, r?ashie Use wl_keyboard_listener and keymap event to get key mapping on Wayland. Weston simple-im.c example is used as a reference implementation and actual key modifiers are derived from Wayland/GDK code from gdkkeys-wayland.c. MozReview-Commit-ID: 9fMwCvxkYy0
config/system-headers.mozbuild
widget/gtk/nsGtkKeyUtils.cpp
widget/gtk/nsGtkKeyUtils.h
--- a/config/system-headers.mozbuild
+++ b/config/system-headers.mozbuild
@@ -1337,11 +1337,12 @@ if CONFIG['MOZ_SYSTEM_ICU']:
 
 if CONFIG['ENABLE_BIGINT']:
     system_headers += [
         'gmp.h'
     ]
 
 if CONFIG['MOZ_WAYLAND']:
     system_headers += [
+        'xkbcommon/xkbcommon.h',
         'wayland-client.h',
         'wayland-egl.h',
     ]
--- a/widget/gtk/nsGtkKeyUtils.cpp
+++ b/widget/gtk/nsGtkKeyUtils.cpp
@@ -23,16 +23,20 @@
 #include "nsGtkUtils.h"
 #include "nsIBidiKeyboard.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
+#ifdef MOZ_WAYLAND
+#include <sys/mman.h>
+#endif
+
 namespace mozilla {
 namespace widget {
 
 LazyLogModule gKeymapWrapperLog("KeymapWrapperWidgets");
 
 #define IS_ASCII_ALPHABETICAL(key) \
     ((('a' <= key) && (key <= 'z')) || (('A' <= key) && (key <= 'Z')))
 
@@ -440,21 +444,213 @@ KeymapWrapper::InitBySystemSettingsX11()
     }
 
     XFreeModifiermap(xmodmap);
     XFree(xkeymap);
 }
 
 #ifdef MOZ_WAYLAND
 void
+KeymapWrapper::SetModifierMask(xkb_keymap *aKeymap, ModifierIndex aModifierIndex,
+                               const char* aModifierName)
+{
+    static auto sXkbKeymapModGetIndex =
+        (xkb_mod_index_t (*)(struct xkb_keymap *, const char *))
+        dlsym(RTLD_DEFAULT, "xkb_keymap_mod_get_index");
+
+    xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName);
+    if (index != XKB_MOD_INVALID) {
+        mModifierMasks[aModifierIndex] = (1 << index);
+    }
+}
+
+void
+KeymapWrapper::SetModifierMasks(xkb_keymap *aKeymap)
+{
+    KeymapWrapper* keymapWrapper = GetInstance();
+
+    // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM);
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT);
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta");
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super");
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper");
+
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock");
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3");
+    keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5");
+
+    MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
+        ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, "
+         "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, "
+         "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X",
+         keymapWrapper,
+         keymapWrapper->GetModifierMask(CAPS_LOCK),
+         keymapWrapper->GetModifierMask(NUM_LOCK),
+         keymapWrapper->GetModifierMask(SCROLL_LOCK),
+         keymapWrapper->GetModifierMask(LEVEL3),
+         keymapWrapper->GetModifierMask(LEVEL5),
+         keymapWrapper->GetModifierMask(SHIFT),
+         keymapWrapper->GetModifierMask(CTRL),
+         keymapWrapper->GetModifierMask(ALT),
+         keymapWrapper->GetModifierMask(META),
+         keymapWrapper->GetModifierMask(SUPER),
+         keymapWrapper->GetModifierMask(HYPER)));
+}
+
+/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c
+*/
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
+                       uint32_t format, int fd, uint32_t size)
+{
+    if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+        close(fd);
+        return;
+    }
+
+    char *mapString = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+    if (mapString == MAP_FAILED) {
+        close(fd);
+        return;
+    }
+
+    static auto sXkbContextNew =
+        (struct xkb_context *(*)(enum xkb_context_flags))
+        dlsym(RTLD_DEFAULT, "xkb_context_new");
+    static auto sXkbKeymapNewFromString =
+        (struct xkb_keymap *(*)(struct xkb_context *, const char *,
+         enum xkb_keymap_format, enum xkb_keymap_compile_flags))
+        dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string");
+
+    struct xkb_context *xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS);
+    struct xkb_keymap *keymap =
+        sXkbKeymapNewFromString(xkb_context, mapString,
+            XKB_KEYMAP_FORMAT_TEXT_V1,
+            XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+    munmap(mapString, size);
+    close(fd);
+
+    if (!keymap) {
+        NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n");
+        return;
+    }
+
+    KeymapWrapper::SetModifierMasks(keymap);
+
+    static auto sXkbKeymapUnRef =
+        (void(*)(struct xkb_keymap *))
+        dlsym(RTLD_DEFAULT, "xkb_keymap_unref");
+    sXkbKeymapUnRef(keymap);
+
+    static auto sXkbContextUnref =
+        (void(*)(struct xkb_context *))
+        dlsym(RTLD_DEFAULT, "xkb_context_unref");
+    sXkbContextUnref(xkb_context);
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+                      uint32_t serial, struct wl_surface *surface,
+                      struct wl_array *keys)
+{
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+                      uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+                    uint32_t serial, uint32_t time, uint32_t key,
+                    uint32_t state)
+{
+}
+
+static void
+keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+                          uint32_t serial, uint32_t mods_depressed,
+                          uint32_t mods_latched, uint32_t mods_locked,
+                          uint32_t group)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+    keyboard_handle_keymap,
+    keyboard_handle_enter,
+    keyboard_handle_leave,
+    keyboard_handle_key,
+    keyboard_handle_modifiers,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+                         unsigned int caps)
+{
+    static wl_keyboard *keyboard = nullptr;
+
+    if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
+        keyboard = wl_seat_get_keyboard(seat);
+        wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
+    } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+        wl_keyboard_destroy(keyboard);
+        keyboard = nullptr;
+    }
+}
+
+static const struct wl_seat_listener seat_listener = {
+      seat_handle_capabilities,
+};
+
+static void
+gdk_registry_handle_global(void               *data,
+                           struct wl_registry *registry,
+                           uint32_t            id,
+                           const char         *interface,
+                           uint32_t            version)
+{
+    if (strcmp(interface, "wl_seat") == 0) {
+        wl_seat *seat =
+            (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+        wl_seat_add_listener(seat, &seat_listener, data);
+    }
+}
+
+static void
+gdk_registry_handle_global_remove(void               *data,
+                                 struct wl_registry *registry,
+                                 uint32_t            id)
+{
+}
+
+static const struct wl_registry_listener keyboard_registry_listener = {
+    gdk_registry_handle_global,
+    gdk_registry_handle_global_remove
+};
+
+void
 KeymapWrapper::InitBySystemSettingsWayland()
 {
-    // Not implemented yet, but at least Alt modifier should be handled to save
-    // popular usage.
-    mModifierMasks[INDEX_ALT] = 1 << 3;
+    // Available as of GTK 3.8+
+    static auto sGdkWaylandDisplayGetWlDisplay =
+        (wl_display *(*)(GdkDisplay *))
+        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+
+    wl_display *display =
+        sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
+    wl_registry_add_listener(wl_display_get_registry(display),
+                             &keyboard_registry_listener, this);
+
+    // Call wl_display_roundtrip() twice to make sure all
+    // callbacks are processed.
+    wl_display_roundtrip(display);
+    wl_display_roundtrip(display);
 }
 #endif
 
 KeymapWrapper::~KeymapWrapper()
 {
     gdk_window_remove_filter(nullptr, FilterEvents, this);
     g_signal_handlers_disconnect_by_func(mGdkKeymap,
                                          FuncToGpointer(OnKeysChanged), this);
--- a/widget/gtk/nsGtkKeyUtils.h
+++ b/widget/gtk/nsGtkKeyUtils.h
@@ -8,16 +8,20 @@
 #ifndef __nsGdkKeyUtils_h__
 #define __nsGdkKeyUtils_h__
 
 #include "nsTArray.h"
 #include "mozilla/EventForwards.h"
 
 #include <gdk/gdk.h>
 #include <X11/XKBlib.h>
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <xkbcommon/xkbcommon.h>
+#endif
 
 namespace mozilla {
 namespace widget {
 
 /**
  *  KeymapWrapper is a wrapper class of GdkKeymap.  GdkKeymap doesn't support
  *  all our needs, therefore, we need to access lower level APIs.
  *  But such code is usually complex and might be slow.  Against such issues,
@@ -145,16 +149,24 @@ public:
      *                          dispatched.  This method should set charCode
      *                          and alternative char codes if it's necessary.
      * @param aGdkKeyEvent      A GdkEventKey instance which caused the
      *                          aKeyEvent.
      */
     static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent,
                                           GdkEventKey* aGdkKeyEvent);
 
+#ifdef MOZ_WAYLAND
+    /**
+     * Utility function to set all supported modifier masks
+     * from xkb_keymap. We call that from Wayland backend routines.
+     */
+    static void SetModifierMasks(xkb_keymap *aKeymap);
+#endif
+
     /**
      * Destroys the singleton KeymapWrapper instance, if it exists.
      */
     static void Shutdown();
 
 protected:
 
     /**
@@ -374,14 +386,23 @@ protected:
                                         GdkEvent* aGdkEvent,
                                         gpointer aData);
 
     /**
      * See the document of WillDispatchKeyboardEvent().
      */
     void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent,
                                            GdkEventKey* aGdkKeyEvent);
+
+#ifdef MOZ_WAYLAND
+    /**
+     * Utility function to set Xkb modifier key mask.
+     */
+    void SetModifierMask(xkb_keymap *aKeymap,
+                         ModifierIndex aModifierIndex,
+                         const char* aModifierName);
+#endif
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* __nsGdkKeyUtils_h__ */