Support wl_keyboard::modifiers event

This event lets the compositor inform clients of the canonical keyboard
modifier/group state.  Make sure we send it at appropriate moments from
the compositor, and listen for it in clients as well.

Signed-off-by: Daniel Stone <daniel@fooishbar.org>
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index bbacea4..332e1c2 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -608,10 +608,20 @@
 	notify_key(&c->base.seat->seat, time, key, state);
 }
 
+static void
+input_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)
+{
+	/* XXX notify_modifiers(); */
+}
+
 static const struct wl_keyboard_listener keyboard_listener = {
 	input_handle_keyboard_enter,
 	input_handle_keyboard_leave,
 	input_handle_key,
+	input_handle_modifiers,
 };
 
 static void
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index 7452c1c..98baa8a 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -568,6 +568,7 @@
 		case XCB_KEY_RELEASE:
 			key_release = (xcb_key_press_event_t *) prev;
 			key_press = (xcb_key_press_event_t *) event;
+			/* XXX use XkbSetDetectableAutoRepeat */
 			if ((event->response_type & ~0x80) == XCB_KEY_PRESS &&
 			    key_release->time == key_press->time &&
 			    key_release->detail == key_press->detail) {
@@ -654,6 +655,7 @@
 			output = x11_compositor_find_output(c, enter_notify->event);
 			x = wl_fixed_from_int(output->base.x + enter_notify->event_x);
 			y = wl_fixed_from_int(output->base.y + enter_notify->event_y);
+			/* XXX notify_modifiers() */
 
 			notify_pointer_focus(&c->base.seat->seat,
 					     &output->base, x, y);
diff --git a/src/compositor.c b/src/compositor.c
index c562546..bfca256 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1654,6 +1654,15 @@
 	wl_keyboard_set_focus(seat->seat.keyboard, &surface->surface);
 	wl_data_device_set_keyboard_focus(&seat->seat);
 
+	if (seat->seat.keyboard->focus_resource) {
+		wl_keyboard_send_modifiers(seat->seat.keyboard->focus_resource,
+					   wl_display_next_serial(compositor->wl_display),
+					   compositor->xkb_info.mods_depressed,
+					   compositor->xkb_info.mods_latched,
+					   compositor->xkb_info.mods_locked,
+					   compositor->xkb_info.group);
+	}
+
 	wl_signal_emit(&compositor->activate_signal, surface);
 }
 
@@ -1822,6 +1831,13 @@
 					      time, key, 0, 0, state);
 
 	grab->interface->key(grab, time, key, state);
+	if (mods)
+		grab->interface->modifiers(grab,
+					   wl_display_get_serial(compositor->wl_display),
+					   compositor->xkb_info.mods_depressed,
+					   compositor->xkb_info.mods_latched,
+					   compositor->xkb_info.mods_locked,
+					   compositor->xkb_info.group);
 }
 
 WL_EXPORT void
@@ -1878,6 +1894,15 @@
 		if (surface) {
 			wl_list_remove(&ws->saved_kbd_focus_listener.link);
 			wl_keyboard_set_focus(ws->seat.keyboard, surface);
+
+			if (seat->keyboard->focus_resource) {
+				wl_keyboard_send_modifiers(seat->keyboard->focus_resource,
+							   wl_display_next_serial(compositor->wl_display),
+							   compositor->xkb_info.mods_depressed,
+							   compositor->xkb_info.mods_latched,
+							   compositor->xkb_info.mods_locked,
+							   compositor->xkb_info.group);
+			}
 			ws->saved_kbd_focus = NULL;
 		}
 	} else {
diff --git a/src/compositor.h b/src/compositor.h
index 09cd215..40301ae 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -292,6 +292,10 @@
 		struct xkb_context *context;
 		struct xkb_keymap *keymap;
 		struct xkb_state *state;
+		uint32_t mods_depressed;
+		uint32_t mods_latched;
+		uint32_t mods_locked;
+		uint32_t group;
 	} xkb_info;
 };
 
diff --git a/src/shell.c b/src/shell.c
index dc4fa47..4d11ba4 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -2451,7 +2451,7 @@
 }
 
 static void
-switcher_destroy(struct switcher *switcher, uint32_t time)
+switcher_destroy(struct switcher *switcher)
 {
 	struct weston_compositor *compositor = switcher->shell->compositor;
 	struct weston_surface *surface;
@@ -2475,17 +2475,26 @@
 	     uint32_t time, uint32_t key, uint32_t state)
 {
 	struct switcher *switcher = container_of(grab, struct switcher, grab);
+
+	if (key == KEY_TAB && state)
+		switcher_next(switcher);
+}
+
+static void
+switcher_modifier(struct wl_keyboard_grab *grab, uint32_t serial,
+		  uint32_t mods_depressed, uint32_t mods_latched,
+		  uint32_t mods_locked, uint32_t group)
+{
+	struct switcher *switcher = container_of(grab, struct switcher, grab);
 	struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
 
-	if ((seat->modifier_state & switcher->shell->binding_modifier) == 0) {
-		switcher_destroy(switcher, time);
-	} else if (key == KEY_TAB && state) {
-		switcher_next(switcher);
-	}
+	if ((seat->modifier_state & switcher->shell->binding_modifier) == 0)
+		switcher_destroy(switcher);
 }
 
 static const struct wl_keyboard_grab_interface switcher_grab = {
-	switcher_key
+	switcher_key,
+	switcher_modifier,
 };
 
 static void
diff --git a/src/util.c b/src/util.c
index 33dd7db..01e5991 100644
--- a/src/util.c
+++ b/src/util.c
@@ -270,8 +270,24 @@
 	}
 }
 
+static void
+binding_modifiers(struct wl_keyboard_grab *grab, uint32_t serial,
+		  uint32_t mods_depressed, uint32_t mods_latched,
+		  uint32_t mods_locked, uint32_t group)
+{
+	struct wl_resource *resource;
+
+	resource = grab->keyboard->focus_resource;
+	if (!resource)
+		return;
+
+	wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+				   mods_latched, mods_locked, group);
+}
+
 static const struct wl_keyboard_grab_interface binding_grab = {
-	binding_key
+	binding_key,
+	binding_modifiers,
 };
 
 static void