+static gboolean wanted_focusevent(XEvent *e)
+{
+ gint mode = e->xfocus.mode;
+ gint detail = e->xfocus.detail;
+ Window win = e->xany.window;
+
+ if (e->type == FocusIn) {
+
+ /* These are ones we never want.. */
+
+ /* This means focus was given by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+ /* This means focus was given back from a keyboard/mouse grab. */
+ if (mode == NotifyUngrab)
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ if (win == RootWindow(ob_display, ob_screen)) {
+ /* This means focus reverted off of a client */
+ if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
+ detail == NotifyInferior)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* This means focus moved from the root window to a client */
+ if (detail == NotifyVirtual)
+ return TRUE;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
+ return TRUE;
+
+ /* This means focus reverted off of a client */
+ if (detail == NotifyInferior)
+ return TRUE;
+
+ /* Otherwise.. */
+ return FALSE;
+ } else {
+ g_assert(e->type == FocusOut);
+
+
+ /* These are ones we never want.. */
+
+ /* This means focus was taken by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+
+ /* Focus left the root window revertedto state */
+ if (win == RootWindow(ob_display, ob_screen))
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ /* This means focus moved from a client to the root window */
+ if (detail == NotifyVirtual)
+ return TRUE;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
+ return TRUE;
+
+ /* Otherwise.. */
+ return FALSE;
+ }
+}
+
+static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
+{
+ return e->type == FocusIn && wanted_focusevent(e);
+}
+
+static gboolean event_ignore(XEvent *e, ObClient *client)
+{
+ switch(e->type) {
+ case FocusIn:
+ case FocusOut:
+ if (!wanted_focusevent(e))
+ return TRUE;
+ break;
+ }
+ return FALSE;
+}
+
+static void event_process(const XEvent *ec, gpointer data)
+{
+ Window window;
+ ObGroup *group = NULL;
+ ObClient *client = NULL;
+ ObDock *dock = NULL;
+ ObDockApp *dockapp = NULL;
+ ObWindow *obwin = NULL;
+ XEvent ee, *e;
+ ObEventData *ed = data;
+
+ /* make a copy we can mangle */
+ ee = *ec;
+ e = ⅇ
+
+ window = event_get_window(e);
+ if (!(e->type == PropertyNotify &&
+ (group = g_hash_table_lookup(group_map, &window))))
+ if ((obwin = g_hash_table_lookup(window_map, &window))) {
+ switch (obwin->type) {
+ case Window_Dock:
+ dock = WINDOW_AS_DOCK(obwin);
+ break;
+ case Window_DockApp:
+ dockapp = WINDOW_AS_DOCKAPP(obwin);
+ break;
+ case Window_Client:
+ client = WINDOW_AS_CLIENT(obwin);
+ break;
+ case Window_Menu:
+ case Window_Internal:
+ /* not to be used for events */
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ event_set_curtime(e);
+ event_hack_mods(e);
+ if (event_ignore(e, client)) {
+ if (ed)
+ ed->ignored = TRUE;
+ return;
+ } else if (ed)
+ ed->ignored = FALSE;
+
+ /* deal with it in the kernel */
+
+ if (menu_frame_visible &&
+ (e->type == EnterNotify || e->type == LeaveNotify))
+ {
+ /* crossing events for menu */
+ event_handle_menu(e);
+ } else if (e->type == FocusIn) {
+ if (e->xfocus.detail == NotifyPointerRoot ||
+ e->xfocus.detail == NotifyDetailNone) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
+ /* Focus has been reverted to the root window or nothing
+ FocusOut events come after UnmapNotify, so we don't need to
+ worry about focusing an invalid window
+ */
+ focus_fallback(TRUE);
+ } else if (e->xfocus.detail == NotifyInferior) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to parent\n");
+ /* Focus has been reverted to parent, which is our frame window,
+ or the root window
+ FocusOut events come after UnmapNotify, so we don't need to
+ worry about focusing an invalid window
+ */
+ focus_fallback(TRUE);
+ } else if (client && client != focus_client) {
+ frame_adjust_focus(client->frame, TRUE);
+ focus_set_client(client);
+ client_calc_layer(client);
+ }
+ } else if (e->type == FocusOut) {
+ gboolean nomove = FALSE;
+ XEvent ce;
+
+ ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
+
+ /* Look for the followup FocusIn */
+ if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
+ /* There is no FocusIn, this means focus went to a window that
+ is not being managed, or a window on another screen. */
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
+ /* nothing is focused */
+ focus_set_client(NULL);
+ } else if (ce.xany.window == e->xany.window) {
+ /* If focus didn't actually move anywhere, there is nothing to do*/
+ nomove = TRUE;
+ } else {
+ /* Focus did move, so process the FocusIn event */
+ ObEventData ed = { .ignored = FALSE };
+ event_process(&ce, &ed);
+ if (ed.ignored) {
+ /* The FocusIn was ignored, this means it was on a window
+ that isn't a client. */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to an unmanaged window 0x%x !\n",
+ ce.xfocus.window);
+ focus_fallback(TRUE);
+ }
+ }
+
+ if (client && !nomove) {
+ frame_adjust_focus(client->frame, FALSE);
+ /* focus_set_client has already been called for sure */
+ client_calc_layer(client);
+ }
+ } else if (group)
+ event_handle_group(group, e);
+ else if (client)
+ event_handle_client(client, e);
+ else if (dockapp)
+ event_handle_dockapp(dockapp, e);
+ else if (dock)
+ event_handle_dock(dock, e);
+ else if (window == RootWindow(ob_display, ob_screen))
+ event_handle_root(e);
+ else if (e->type == MapRequest)
+ client_manage(window);
+ else if (e->type == ConfigureRequest) {
+ /* unhandled configure requests must be used to configure the
+ window directly */
+ XWindowChanges xwc;
+
+ xwc.x = e->xconfigurerequest.x;
+ xwc.y = e->xconfigurerequest.y;
+ xwc.width = e->xconfigurerequest.width;
+ xwc.height = e->xconfigurerequest.height;
+ xwc.border_width = e->xconfigurerequest.border_width;
+ xwc.sibling = e->xconfigurerequest.above;
+ xwc.stack_mode = e->xconfigurerequest.detail;
+
+ /* we are not to be held responsible if someone sends us an
+ invalid request! */
+ xerror_set_ignore(TRUE);
+ XConfigureWindow(ob_display, window,
+ e->xconfigurerequest.value_mask, &xwc);
+ xerror_set_ignore(FALSE);
+ }
+
+ /* user input (action-bound) events */
+ if (e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify || e->type == KeyPress ||
+ e->type == KeyRelease)
+ {
+ if (menu_frame_visible)
+ event_handle_menu(e);
+ else {
+ if (!keyboard_process_interactive_grab(e, &client)) {
+ if (moveresize_in_progress) {
+ moveresize_event(e);
+
+ /* make further actions work on the client being
+ moved/resized */
+ client = moveresize_client;
+ }
+
+ menu_can_hide = FALSE;
+ ob_main_loop_timeout_add(ob_main_loop,
+ config_menu_hide_delay * 1000,
+ menu_hide_delay_func,
+ NULL, g_direct_equal, NULL);
+
+ if (e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify) {
+ mouse_event(client, e);
+ } else if (e->type == KeyPress) {
+ keyboard_event((focus_cycle_target ? focus_cycle_target :
+ client), e);
+ }
+ }
+ }
+ }
+ /* if something happens and it's not from an XEvent, then we don't know
+ the time */
+ event_curtime = CurrentTime;
+}
+