Bug 1470047 - Implement InitBySystemSettingsWayland() and get key modifiers on Wayland, r=ashie
authorMartin Stransky <stransky@redhat.com>
Tue, 10 Jul 2018 18:28:32 +0200
changeset 484045 5e46c8d0ed58491ddfc7ee6d95754b226f743436
parent 484044 a43b17c3356ca19f91a5ccd946688d2cb97a1ada
child 484046 9c3aeaeee7ec77393c3ece92d3274c58d8f0bb8c
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersashie
bugs1470047
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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__ */