Nested X11: use KeymapNotify events to fix key state on FocusIn

If the user Alt-Tabs out of the window, we will be left thinking
the Alt key is still pressed since we don't see a release for it.

Solve this and other related issues for the nested X11 compositor
by selecting for KeymapStateMask which causes a KeymapNotify event
to be sent after each FocusIn, and when we get these events, update
the internal XKB state and send any necessary modifiers events to
clients.

https://bugzilla.gnome.org/show_bug.cgi?id=753948
This commit is contained in:
Owen W. Taylor 2015-08-21 16:25:53 -04:00
parent 1d56d50fcd
commit 614d6bd0f8
5 changed files with 87 additions and 0 deletions

View File

@ -40,6 +40,7 @@
#include "meta-idle-monitor-xsync.h" #include "meta-idle-monitor-xsync.h"
#include "meta-monitor-manager-xrandr.h" #include "meta-monitor-manager-xrandr.h"
#include "backends/meta-monitor-manager-dummy.h" #include "backends/meta-monitor-manager-dummy.h"
#include "wayland/meta-wayland.h"
#include "meta-cursor-renderer-x11.h" #include "meta-cursor-renderer-x11.h"
#include <meta/util.h> #include <meta/util.h>
@ -269,6 +270,21 @@ handle_host_xevent (MetaBackend *backend,
} }
} }
if (priv->mode == META_BACKEND_X11_MODE_NESTED && event->type == FocusIn)
{
Window xwin = meta_backend_x11_get_xwindow(x11);
XEvent xev;
if (event->xfocus.window == xwin)
{
/* Since we've selected for KeymapStateMask, every FocusIn is followed immediately
* by a KeymapNotify event */
XMaskEvent(priv->xdisplay, KeymapStateMask, &xev);
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
meta_wayland_compositor_update_key_state (compositor, xev.xkeymap.key_vector, 32, 8);
}
}
if (event->type == (priv->xsync_event_base + XSyncAlarmNotify)) if (event->type == (priv->xsync_event_base + XSyncAlarmNotify))
handle_alarm_notify (backend, event); handle_alarm_notify (backend, event);
@ -800,6 +816,20 @@ meta_backend_x11_select_stage_events (MetaBackend *backend)
} }
XISelectEvents (priv->xdisplay, xwin, &mask, 1); XISelectEvents (priv->xdisplay, xwin, &mask, 1);
if (priv->mode == META_BACKEND_X11_MODE_NESTED)
{
/* We have no way of tracking key changes when the stage doesn't have
* focus, so we select for KeymapStateMask so that we get a complete
* dump of the keyboard state in a KeymapNotify event that immediately
* follows each FocusIn (and EnterNotify, but we ignore that.)
*/
XWindowAttributes xwa;
XGetWindowAttributes(priv->xdisplay, xwin, &xwa);
XSelectInput(priv->xdisplay, xwin,
xwa.your_event_mask | FocusChangeMask | KeymapStateMask);
}
} }
static void static void

View File

@ -477,6 +477,33 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
return handled; return handled;
} }
void
meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *keyboard,
char *key_vector,
int key_vector_len,
int offset)
{
gboolean mods_changed = FALSE;
for (gint i = offset; i < key_vector_len * 8; i++)
{
gboolean set = (key_vector[i/8] & (1 << (i % 8))) != 0;
/* The 'offset' parameter allows the caller to have the indices
* into key_vector to either be X-style (base 8) or evdev (base 0), or
* something else (unlikely). We subtract 'offset' to convert to evdev
* style, then add 8 to convert the "evdev" style keycode back to
* the X-style that xkbcommon expects.
*/
mods_changed |= xkb_state_update_key (keyboard->xkb_info.state,
i - offset + 8,
set ? XKB_KEY_DOWN : XKB_KEY_UP);
}
if (mods_changed)
notify_modifiers (keyboard);
}
static void static void
move_resources (struct wl_list *destination, struct wl_list *source) move_resources (struct wl_list *destination, struct wl_list *source)
{ {

View File

@ -85,6 +85,10 @@ void meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard,
gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
const ClutterKeyEvent *event); const ClutterKeyEvent *event);
void meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *compositor,
char *key_vector,
int key_vector_len,
int offset);
void meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard, void meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
MetaWaylandSurface *surface); MetaWaylandSurface *surface);

View File

@ -207,6 +207,28 @@ meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor,
return meta_wayland_seat_handle_event (compositor->seat, event); return meta_wayland_seat_handle_event (compositor->seat, event);
} }
/* meta_wayland_compositor_update_key_state:
* @compositor: the #MetaWaylandCompositor
* @key_vector: bit vector of key states
* @key_vector_len: length of @key_vector
* @offset: the key for the first evdev keycode is found at this offset in @key_vector
*
* This function is used to resynchronize the key state that Mutter
* is tracking with the actual keyboard state. This is useful, for example,
* to handle changes in key state when a nested compositor doesn't
* have focus. We need to fix up the XKB modifier tracking and deliver
* any modifier changes to clients.
*/
void
meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor,
char *key_vector,
int key_vector_len,
int offset)
{
meta_wayland_keyboard_update_key_state (&compositor->seat->keyboard,
key_vector, key_vector_len, offset);
}
void void
meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor, meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface) MetaWaylandSurface *surface)

View File

@ -39,6 +39,10 @@ void meta_wayland_compositor_update (MetaWaylandComp
const ClutterEvent *event); const ClutterEvent *event);
gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor,
const ClutterEvent *event); const ClutterEvent *event);
void meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor,
char *key_vector,
int key_vector_len,
int offset);
void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor); void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor);
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,