+ return ret;
+}
+
+static void 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(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;
+ }
+
+ /* 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)) {
+ 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))
+ mouse_event(client, e);
+ } else
+ keyboard_event((focus_cycle_target ? focus_cycle_target :
+ (client ? client : focus_client)), e);
+ }
+}
+
+static void focus_delay_dest(gpointer data)
+{
+ g_free(data);
+}
+
+static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
+{
+ const ObFocusDelayData *f1 = d1;
+ return f1->client == d2;
+}
+
+static gboolean focus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime;
+
+ /* don't move focus and kill the menu or the move/resize */
+ if (menu_frame_visible || moveresize_in_progress) return FALSE;
+
+ 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 void focus_delay_client_dest(ObClient *client, gpointer data)
+{
+ ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
+ client, FALSE);
+}
+
+void event_halt_focus_delay()
+{
+ /* ignore all enter events up till the event which caused this to occur */
+ if (event_curserial) event_ignore_enter_range(1, event_curserial);
+ ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+}
+
+gulong event_start_ignore_all_enters(void)
+{
+ XSync(ob_display, FALSE);
+ return LastKnownRequestProcessed(ob_display);
+}
+
+static void event_ignore_enter_range(gulong start, gulong end)
+{
+ ObSerialRange *r;
+
+ g_assert(start != 0);
+ g_assert(end != 0);
+
+ r = g_new(ObSerialRange, 1);
+ 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\n",
+ r->start, r->end);
+
+ /* increment the serial so we don't ignore events we weren't meant to */
+ XSync(ob_display, FALSE);
+}
+
+void event_end_ignore_all_enters(gulong start)
+{
+ XSync(ob_display, FALSE);
+ event_ignore_enter_range(start, LastKnownRequestProcessed(ob_display));
+}
+
+static gboolean is_enter_focus_event_ignored(XEvent *e)
+{
+ GSList *it, *next;
+
+ g_assert(e->type == EnterNotify &&
+ !(e->xcrossing.mode == NotifyGrab ||
+ e->xcrossing.mode == NotifyUngrab ||
+ e->xcrossing.detail == NotifyInferior));
+
+ for (it = ignore_serials; it; it = next) {
+ ObSerialRange *r = it->data;
+
+ next = g_slist_next(it);
+
+ if ((glong)(e->xany.serial - r->end) > 0) {
+ /* past the end */
+ ignore_serials = g_slist_delete_link(ignore_serials, it);
+ g_free(r);
+ }
+ else if ((glong)(e->xany.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\n");
+ }
+ else if (menu_frame_visible) {
+ menu_frame_hide_all();
+ ob_debug("KILLED open menus\n");
+ }
+ else if (moveresize_in_progress) {
+ moveresize_end(TRUE);
+ ob_debug("KILLED interactive moveresize\n");
+ }
+ else if (grab_on_keyboard()) {
+ ungrab_keyboard();
+ ob_debug("KILLED active grab on keyboard\n");
+ }
+ else
+ ungrab_passive_key();
+}
+
+gboolean event_time_after(Time t1, Time 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 half of the number space of a Time type variable */
+#define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
+
+ 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);
+}
+
+Time event_get_server_time(void)
+{
+ /* Generate a timestamp */
+ XEvent event;
+
+ XChangeProperty(ob_display, screen_support_win,
+ prop_atoms.wm_class, prop_atoms.string,
+ 8, PropModeAppend, NULL, 0);
+ XWindowEvent(ob_display, screen_support_win, PropertyChangeMask, &event);
+ return event.xproperty.time;