From 8e7c8b28e5c4968303e5bd5fe8b9e7a16f5de56f Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Fri, 13 Sep 2013 16:10:49 +0900 Subject: [PATCH] Bug 768287 Initialize key event's modifier state on GTK with next XKB state change event r=karlt --- widget/gtk2/nsGtkKeyUtils.cpp | 75 +++++++++++++++++++++++++++++------ widget/gtk2/nsGtkKeyUtils.h | 6 +++ 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/widget/gtk2/nsGtkKeyUtils.cpp b/widget/gtk2/nsGtkKeyUtils.cpp index 5b869cbdd190..f924ebe24fdc 100644 --- a/widget/gtk2/nsGtkKeyUtils.cpp +++ b/widget/gtk2/nsGtkKeyUtils.cpp @@ -12,12 +12,11 @@ #include #include #include -#ifdef MOZ_X11 #include -#endif /* MOZ_X11 */ #if (MOZ_WIDGET_GTK == 3) #include #endif +#include #include "nsGUIEvent.h" #include "WidgetUtils.h" #include "keysym2ucs.h" @@ -153,7 +152,8 @@ KeymapWrapper::GetInstance() } KeymapWrapper::KeymapWrapper() : - mInitialized(false), mGdkKeymap(gdk_keymap_get_default()) + mInitialized(false), mGdkKeymap(gdk_keymap_get_default()), + mXKBBaseEventCode(0) { #ifdef PR_LOGGING if (!gKeymapWrapperLog) { @@ -171,6 +171,8 @@ KeymapWrapper::KeymapWrapper() : g_object_weak_ref(G_OBJECT(mGdkKeymap), (GWeakNotify)OnDestroyKeymap, this); + InitXKBExtension(); + Init(); } @@ -204,6 +206,48 @@ KeymapWrapper::Init() GetModifierMask(SUPER), GetModifierMask(HYPER))); } +void +KeymapWrapper::InitXKBExtension() +{ + int xkbMajorVer = XkbMajorVersion; + int xkbMinorVer = XkbMinorVersion; + if (!XkbLibraryVersion(&xkbMajorVer, &xkbMinorVer)) { + PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, + ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " + "XkbLibraryVersion()", this)); + return; + } + + Display* display = + gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + // XkbLibraryVersion() set xkbMajorVer and xkbMinorVer to that of the + // library, which may be newer than what is required of the server in + // XkbQueryExtension(), so these variables should be reset to + // XkbMajorVersion and XkbMinorVersion before the XkbQueryExtension call. + xkbMajorVer = XkbMajorVersion; + xkbMinorVer = XkbMinorVersion; + int opcode, baseErrorCode; + if (!XkbQueryExtension(display, &opcode, &mXKBBaseEventCode, &baseErrorCode, + &xkbMajorVer, &xkbMinorVer)) { + PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, + ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " + "XkbQueryExtension(), display=0x%p", this, display)); + return; + } + + if (!XkbSelectEventDetails(display, XkbUseCoreKbd, XkbStateNotify, + XkbModifierStateMask, XkbModifierStateMask)) { + PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, + ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " + "XkbSelectEventDetails(), display=0x%p", this, display)); + return; + } + + PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, + ("KeymapWrapper(%p): InitXKBExtension, Succeeded", this)); +} + void KeymapWrapper::InitBySystemSettings() { @@ -722,17 +766,22 @@ KeymapWrapper::InitKeyEvent(nsKeyEvent& aKeyEvent, // Unfortunately, gdk_keyboard_get_modifiers() returns current modifier // state. It means if there're some pending modifier key press or // key release events, the result isn't what we want. - // Temporarily, we should compute the state only when the key event - // is GDK_KEY_PRESS. - // XXX If we could know the modifier keys state at the key release event, - // we should cut out changingMask from modifierState. guint modifierState = aGdkKeyEvent->state; - if (aGdkKeyEvent->is_modifier && aGdkKeyEvent->type == GDK_KEY_PRESS) { - ModifierKey* modifierKey = - keymapWrapper->GetModifierKey(aGdkKeyEvent->hardware_keycode); - if (modifierKey) { - // If new modifier key is pressed, add the pressed mod mask. - modifierState |= modifierKey->mMask; + if (aGdkKeyEvent->is_modifier) { + Display* display = + gdk_x11_display_get_xdisplay(gdk_display_get_default()); + if (XEventsQueued(display, QueuedAfterReading)) { + XEvent nextEvent; + XPeekEvent(display, &nextEvent); + if (nextEvent.type == keymapWrapper->mXKBBaseEventCode) { + XkbEvent* XKBEvent = (XkbEvent*)&nextEvent; + if (XKBEvent->any.xkb_type == XkbStateNotify) { + XkbStateNotifyEvent* stateNotifyEvent = + (XkbStateNotifyEvent*)XKBEvent; + modifierState &= ~0xFF; + modifierState |= stateNotifyEvent->lookup_mods; + } + } } } InitInputEvent(aKeyEvent, modifierState); diff --git a/widget/gtk2/nsGtkKeyUtils.h b/widget/gtk2/nsGtkKeyUtils.h index 3660283801a2..3b3c1a9d251f 100644 --- a/widget/gtk2/nsGtkKeyUtils.h +++ b/widget/gtk2/nsGtkKeyUtils.h @@ -139,6 +139,7 @@ protected: * Initializing methods. */ void Init(); + void InitXKBExtension(); void InitBySystemSettings(); /** @@ -198,6 +199,11 @@ protected: */ GdkKeymap* mGdkKeymap; + /** + * The base event code of XKB extension. + */ + int mXKBBaseEventCode; + /** * Pointer of the singleton instance. */