X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fevent.c;h=6f819cc1f4c0838524d03fdd590d416e95fb3e23;hb=7c1fb96bd33a97c9be55e5fcfae6cf7d625acbb6;hp=d4d921efd229cb34d94af6b23cfcaaf9db8a4513;hpb=adc5675823de8e3bbe94a1419b04103c6f845dad;p=chaz%2Fopenbox diff --git a/openbox/event.c b/openbox/event.c index d4d921ef..6f819cc1 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -64,6 +64,12 @@ typedef struct gboolean ignored; } ObEventData; +typedef struct +{ + ObClient *client; + Time time; +} ObFocusDelayData; + static void event_process(const XEvent *e, gpointer data); static void event_client_dest(ObClient *client, gpointer data); static void event_handle_root(XEvent *e); @@ -73,6 +79,8 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); static void event_handle_group(ObGroup *g, XEvent *e); +static void focus_delay_dest(gpointer data); +static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2); static gboolean focus_delay_func(gpointer data); static void focus_delay_client_dest(ObClient *client, gpointer data); @@ -312,6 +320,7 @@ 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) { @@ -326,6 +335,15 @@ static gboolean wanted_focusevent(XEvent *e) /* 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; @@ -333,6 +351,10 @@ static gboolean wanted_focusevent(XEvent *e) if (detail == NotifyNonlinearVirtual) return TRUE; + /* This means focus reverted off of a client */ + if (detail == NotifyInferior) + return TRUE; + /* Otherwise.. */ return FALSE; } else { @@ -345,6 +367,10 @@ static gboolean wanted_focusevent(XEvent *e) 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 */ @@ -367,19 +393,13 @@ static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg) static gboolean event_ignore(XEvent *e, ObClient *client) { switch(e->type) { - case EnterNotify: - case LeaveNotify: - if (e->xcrossing.detail == NotifyInferior) - return TRUE; - break; case FocusIn: case FocusOut: - /* I don't think this should ever happen with our event masks, but - if it does, we don't want it. */ - if (client == NULL) - return TRUE; - if (!wanted_focusevent(e)) + if (!wanted_focusevent(e)) { + ob_debug_type(OB_DEBUG_FOCUS, "focus event ignored\n"); return TRUE; + } + ob_debug_type(OB_DEBUG_FOCUS, "focus event used;\n"); break; } return FALSE; @@ -422,27 +442,28 @@ static void event_process(const XEvent *ec, gpointer data) } } -#if 1 /* focus debugging stuff */ if (e->type == FocusIn || e->type == FocusOut) { gint mode = e->xfocus.mode; gint detail = e->xfocus.detail; Window window = e->xfocus.window; if (detail == NotifyVirtual) { - ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n", - (e->type == FocusIn ? "IN" : "OUT"), window); + ob_debug_type(OB_DEBUG_FOCUS, + "FOCUS %s NOTIFY VIRTUAL window 0x%x\n", + (e->type == FocusIn ? "IN" : "OUT"), window); } else if (detail == NotifyNonlinearVirtual) { - ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n", - (e->type == FocusIn ? "IN" : "OUT"), window); + ob_debug_type(OB_DEBUG_FOCUS, + "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n", + (e->type == FocusIn ? "IN" : "OUT"), window); } else - ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n", - (e->type == FocusIn ? "IN" : "OUT"), - detail, mode, window); + ob_debug_type(OB_DEBUG_FOCUS, + "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n", + (e->type == FocusIn ? "IN" : "OUT"), + detail, mode, window); } -#endif event_set_curtime(e); event_hack_mods(e); @@ -454,7 +475,65 @@ static void event_process(const XEvent *ec, gpointer data) ed->ignored = FALSE; /* deal with it in the kernel */ - if (group) + + 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, so fall + back to something other than the window which just had it. */ + focus_fallback(FALSE); + } 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, so fall back to something other than the + window which had it. */ + focus_fallback(FALSE); + } else if (client && client != focus_client) { + focus_set_client(client); + frame_adjust_focus(client->frame, TRUE); + 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"); + } 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) { + /* This client is no longer focused, so show that */ + focus_hilite = NULL; + frame_adjust_focus(client->frame, FALSE); + client_calc_layer(client); + } + } else if (group) event_handle_group(group, e); else if (client) event_handle_client(client, e); @@ -508,12 +587,12 @@ static void event_process(const XEvent *ec, gpointer data) ob_main_loop_timeout_add(ob_main_loop, config_menu_hide_delay * 1000, menu_hide_delay_func, - NULL, NULL); + NULL, g_direct_equal, NULL); if (e->type == ButtonPress || e->type == ButtonRelease || - e->type == MotionNotify) + e->type == MotionNotify) { mouse_event(client, e); - else if (e->type == KeyPress) { + } else if (e->type == KeyPress) { keyboard_event((focus_cycle_target ? focus_cycle_target : (focus_hilite ? focus_hilite : client)), e); @@ -542,8 +621,11 @@ static void event_handle_root(XEvent *e) msgtype = e->xclient.message_type; if (msgtype == prop_atoms.net_current_desktop) { guint d = e->xclient.data.l[0]; - if (d < screen_num_desktops) + if (d < screen_num_desktops) { + event_curtime = e->xclient.data.l[1]; + ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime); screen_set_desktop(d); + } } else if (msgtype == prop_atoms.net_number_of_desktops) { guint d = e->xclient.data.l[0]; if (d > 0) @@ -590,13 +672,24 @@ void event_enter_client(ObClient *client) if (client_normal(client) && client_can_focus(client)) { if (config_focus_delay) { + ObFocusDelayData *data; + ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func); + + data = g_new(ObFocusDelayData, 1); + data->client = client; + data->time = event_curtime; + ob_main_loop_timeout_add(ob_main_loop, config_focus_delay, focus_delay_func, - client, NULL); - } else - focus_delay_func(client); + data, focus_delay_cmp, focus_delay_dest); + } else { + ObFocusDelayData data; + data.client = client; + data.time = event_curtime; + focus_delay_func(&data); + } } } @@ -645,43 +738,6 @@ static void event_handle_client(ObClient *client, XEvent *e) } } break; - case FocusIn: - if (client != focus_client) { - focus_set_client(client); - frame_adjust_focus(client->frame, TRUE); - client_calc_layer(client); - } - break; - case FocusOut: - /* 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. most likely, this went to PointerRoot - or None, meaning the window is no longer around so fallback - focus, but not to that window */ - ob_debug("Focus went to a black hole !\n"); - focus_fallback(FALSE); - } else if (ce.xany.window == e->xany.window) { - /* If focus didn't actually move anywhere, there is nothing to do*/ - break; - } else { - /* Focus did move, so process the FocusIn event */ - ObEventData ed; - 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("Focus went to an unmanaged window 0x%x !\n", - ce.xfocus.window); - focus_fallback(TRUE); - } - } - - /* This client is no longer focused, so show that */ - focus_hilite = NULL; - frame_adjust_focus(client->frame, FALSE); - client_calc_layer(client); - break; case LeaveNotify: con = frame_context(client, e->xcrossing.window); switch (con) { @@ -706,10 +762,12 @@ static void event_handle_client(ObClient *client, XEvent *e) frame_adjust_state(client->frame); break; case OB_FRAME_CONTEXT_FRAME: + if (keyboard_interactively_grabbed()) + break; if (config_focus_follow && config_focus_delay) ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, - client, TRUE); + client, FALSE); break; default: break; @@ -747,24 +805,24 @@ static void event_handle_client(ObClient *client, XEvent *e) frame_adjust_state(client->frame); break; case OB_FRAME_CONTEXT_FRAME: + if (keyboard_interactively_grabbed()) + break; if (e->xcrossing.mode == NotifyGrab || e->xcrossing.mode == NotifyUngrab) { -#ifdef DEBUG_FOCUS - ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n", - (e->type == EnterNotify ? "Enter" : "Leave"), - e->xcrossing.mode, - e->xcrossing.detail, client?client->window:0); -#endif + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx IGNORED\n", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, client?client->window:0); } else { -#ifdef DEBUG_FOCUS - ob_debug("%sNotify mode %d detail %d on %lx, " - "focusing window: %d\n", - (e->type == EnterNotify ? "Enter" : "Leave"), - e->xcrossing.mode, - e->xcrossing.detail, (client?client->window:0), - !nofocus); -#endif + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx, " + "focusing window: %d\n", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, (client?client->window:0), + !nofocus); if (!nofocus && config_focus_follow) event_enter_client(client); } @@ -884,9 +942,14 @@ static void event_handle_client(ObClient *client, XEvent *e) client->ignore_unmaps--; break; } + ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d " + "ignores left %d\n", + client->window, e->xunmap.event, e->xunmap.from_configure, + client->ignore_unmaps); client_unmanage(client); break; case DestroyNotify: + ob_debug("DestroyNotify for window 0x%x\n", client->window); client_unmanage(client); break; case ReparentNotify: @@ -904,6 +967,7 @@ static void event_handle_client(ObClient *client, XEvent *e) X server to deal with after we unmanage the window */ XPutBackEvent(ob_display, e); + ob_debug("ReparentNotify for window 0x%x\n", client->window); client_unmanage(client); break; case MapRequest: @@ -913,7 +977,7 @@ static void event_handle_client(ObClient *client, XEvent *e) it can happen now when the window is on another desktop, but we still don't want it! */ - client_activate(client, FALSE, TRUE, CurrentTime); + client_activate(client, FALSE, TRUE); break; case ClientMessage: /* validate cuz we query stuff off the client here */ @@ -972,11 +1036,11 @@ static void event_handle_client(ObClient *client, XEvent *e) (e->xclient.data.l[0] == 0 ? "unknown" : (e->xclient.data.l[0] == 1 ? "application" : (e->xclient.data.l[0] == 2 ? "user" : "INVALID")))); - /* XXX make use of data.l[1] and [2] ! */ + /* XXX make use of data.l[2] ! */ + event_curtime = e->xclient.data.l[1]; client_activate(client, FALSE, (e->xclient.data.l[0] == 0 || - e->xclient.data.l[0] == 2), - e->xclient.data.l[1]); + e->xclient.data.l[0] == 2)); } else if (msgtype == prop_atoms.net_wm_moveresize) { ob_debug("net_wm_moveresize for 0x%lx direction %d\n", client->window, e->xclient.data.l[2]); @@ -1127,7 +1191,7 @@ static void event_handle_client(ObClient *client, XEvent *e) client_update_icons(client); } else if (msgtype == prop_atoms.net_wm_user_time) { - client_update_user_time(client, TRUE); + client_update_user_time(client); } else if (msgtype == prop_atoms.sm_client_id) { client_update_sm_client_id(client); @@ -1226,24 +1290,25 @@ static void event_handle_menu(XEvent *ev) menu_frame_hide_all(); } break; - case MotionNotify: - if ((f = menu_frame_under(ev->xmotion.x_root, - ev->xmotion.y_root))) { - menu_frame_move_on_screen(f); - if ((e = menu_entry_frame_under(ev->xmotion.x_root, - ev->xmotion.y_root))) - menu_frame_select(f, e); + case EnterNotify: + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) { + if (e->ignore_enters) + --e->ignore_enters; + else + menu_frame_select(e->frame, e); } + break; + case LeaveNotify: + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) && + (f = find_active_menu()) && f->selected == e && + e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU) { - ObMenuFrame *a; - - a = find_active_menu(); - if (a && a != f && - a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU) - { - menu_frame_select(a, NULL); - } + menu_frame_select(e->frame, NULL); } + case MotionNotify: + if ((e = menu_entry_frame_under(ev->xmotion.x_root, + ev->xmotion.y_root))) + menu_frame_select(e->frame, e); break; case KeyPress: if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) @@ -1280,24 +1345,35 @@ static gboolean menu_hide_delay_func(gpointer data) return FALSE; /* no repeat */ } +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) { - ObClient *c = data; + ObFocusDelayData *d = data; + Time old = event_curtime; - if (focus_client != c) { - if (client_validate(c)) { - client_focus(c); - if (config_focus_raise) - client_raise(c); - } + event_curtime = d->time; + if (focus_client != d->client) { + if (client_focus(d->client) && config_focus_raise) + client_raise(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, TRUE); + client, FALSE); } static void event_client_dest(ObClient *client, gpointer data) @@ -1341,3 +1417,28 @@ void event_ignore_queued_enters() } g_slist_free(saved); } + +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); +}