+ break;
+ case LeaveNotify:
+ /*ignore leaves when we're already in the window */
+ if (ev->xcrossing.detail == NotifyInferior)
+ break;
+
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
+ /* check if an EnterNotify event is coming, and if not, then select
+ nothing in the menu */
+ if (!xqueue_exists_local(event_look_for_menu_enter, e->frame))
+ menu_frame_select(e->frame, NULL, FALSE);
+ }
+ break;
+ }
+}
+
+static gboolean event_handle_user_input(ObClient *client, XEvent *e)
+{
+ g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify || e->type == KeyPress ||
+ e->type == KeyRelease);
+
+ if (menu_frame_visible) {
+ if (event_handle_menu_input(e))
+ /* don't use the event if the menu used it, but if the menu
+ didn't use it and it's a keypress that is bound, it will
+ close the menu and be used */
+ return TRUE;
+ }
+
+ /* if the keyboard interactive action uses the event then dont
+ use it for bindings. likewise is moveresize uses the event. */
+ if (actions_interactive_input_event(e) || moveresize_event(e))
+ return TRUE;
+
+ if (moveresize_in_progress)
+ /* make further actions work on the client being
+ moved/resized */
+ client = moveresize_client;
+
+ if (e->type == ButtonPress ||
+ e->type == ButtonRelease ||
+ e->type == MotionNotify)
+ {
+ /* the frame may not be "visible" but they can still click on it
+ in the case where it is animating before disappearing */
+ if (!client || !frame_iconify_animating(client->frame))
+ return mouse_event(client, e);
+ } else
+ return keyboard_event((focus_cycle_target ? focus_cycle_target :
+ (client ? client : focus_client)), e);
+
+ return FALSE;
+}
+
+static void focus_delay_dest(gpointer data)
+{
+ g_slice_free(ObFocusDelayData, data);
+ focus_delay_timeout_id = 0;
+ focus_delay_timeout_client = NULL;
+}
+
+static void unfocus_delay_dest(gpointer data)
+{
+ g_slice_free(ObFocusDelayData, data);
+ unfocus_delay_timeout_id = 0;
+ unfocus_delay_timeout_client = NULL;
+}
+
+static gboolean focus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime; /* save the curtime */
+
+ event_curtime = d->time;
+ event_curserial = d->serial;
+ if (client_focus(d->client) && config_focus_raise)
+ stacking_raise(CLIENT_AS_WINDOW(d->client));
+ event_curtime = old;
+ return FALSE; /* no repeat */
+}
+
+static gboolean unfocus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime; /* save the curtime */
+
+ event_curtime = d->time;
+ event_curserial = d->serial;
+ focus_nothing();
+ event_curtime = old;
+ return FALSE; /* no repeat */
+}
+
+static void focus_delay_client_dest(ObClient *client, gpointer data)
+{
+ if (focus_delay_timeout_client == client && focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_client == client && unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
+}
+
+void event_halt_focus_delay(void)
+{
+ /* ignore all enter events up till the event which caused this to occur */
+ if (event_curserial) event_ignore_enter_range(1, event_curserial);
+ if (focus_delay_timeout_id) g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_id) g_source_remove(unfocus_delay_timeout_id);
+}
+
+gulong event_start_ignore_all_enters(void)
+{
+ return NextRequest(obt_display);
+}
+
+static void event_ignore_enter_range(gulong start, gulong end)
+{
+ ObSerialRange *r;
+
+ g_assert(start != 0);
+ g_assert(end != 0);
+
+ r = g_slice_new(ObSerialRange);
+ r->start = start;
+ r->end = end;
+ ignore_serials = g_slist_prepend(ignore_serials, r);
+
+ ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu",
+ r->start, r->end);
+
+ /* increment the serial so we don't ignore events we weren't meant to */
+ OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS);
+}
+
+void event_end_ignore_all_enters(gulong start)
+{
+ /* Use (NextRequest-1) so that we ignore up to the current serial only.
+ Inside event_ignore_enter_range, we increment the serial by one, but if
+ we ignore that serial too, then any enter events generated by mouse
+ movement will be ignored until we create some further network traffic.
+ Instead ignore up to NextRequest-1, then when we increment the serial,
+ we will be *past* the range of ignored serials */
+ event_ignore_enter_range(start, NextRequest(obt_display)-1);
+}
+
+static gboolean is_enter_focus_event_ignored(gulong serial)
+{
+ GSList *it, *next;
+
+ for (it = ignore_serials; it; it = next) {
+ ObSerialRange *r = it->data;
+
+ next = g_slist_next(it);
+
+ if ((glong)(serial - r->end) > 0) {
+ /* past the end */
+ ignore_serials = g_slist_delete_link(ignore_serials, it);
+ g_slice_free(ObSerialRange, r);
+ }
+ else if ((glong)(serial - r->start) >= 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void event_cancel_all_key_grabs(void)
+{
+ if (actions_interactive_act_running()) {
+ actions_interactive_cancel_act();
+ ob_debug("KILLED interactive action");
+ }
+ else if (menu_frame_visible) {
+ menu_frame_hide_all();
+ ob_debug("KILLED open menus");
+ }
+ else if (moveresize_in_progress) {
+ moveresize_end(TRUE);
+ ob_debug("KILLED interactive moveresize");
+ }
+ else if (grab_on_keyboard()) {
+ ungrab_keyboard();
+ ob_debug("KILLED active grab on keyboard");
+ }
+ else
+ ungrab_passive_key();
+
+ XSync(obt_display, FALSE);
+}
+
+gboolean event_time_after(guint32 t1, guint32 t2)
+{
+ g_assert(t1 != CurrentTime);
+ g_assert(t2 != CurrentTime);
+
+ /*
+ Timestamp values wrap around (after about 49.7 days). The server, given
+ its current time is represented by timestamp T, always interprets
+ timestamps from clients by treating half of the timestamp space as being
+ later in time than T.
+ - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
+ */
+
+ /* TIME_HALF is not half of the number space of a Time type variable.
+ * Rather, it is half the number space of a timestamp value, which is
+ * always 32 bits. */
+#define TIME_HALF (guint32)(1 << 31)
+
+ if (t2 >= TIME_HALF)
+ /* t2 is in the second half so t1 might wrap around and be smaller than
+ t2 */
+ return t1 >= t2 || t1 < (t2 + TIME_HALF);
+ else
+ /* t2 is in the first half so t1 has to come after it */
+ return t1 >= t2 && t1 < (t2 + TIME_HALF);
+}
+
+gboolean find_timestamp(XEvent *e, gpointer data)
+{
+ const Time t = event_get_timestamp(e);
+ if (t && t >= event_curtime) {
+ event_curtime = t;
+ return TRUE;