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);
static gboolean menu_can_hide;
+static ObFocusDelayData focus_delay_data = { .client = NULL,
+ .time = CurrentTime };
+
+
+
#ifdef USE_SM
static void ice_handler(gint fd, gpointer conn)
{
{
gint mode = e->xfocus.mode;
gint detail = e->xfocus.detail;
+ Window win = e->xany.window;
if (e->type == FocusIn) {
/* 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;
if (detail == NotifyNonlinearVirtual)
return TRUE;
+ /* This means focus reverted off of a client */
+ if (detail == NotifyInferior)
+ return TRUE;
+
/* Otherwise.. */
return FALSE;
} else {
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 */
switch(e->type) {
case EnterNotify:
case LeaveNotify:
- if (e->xcrossing.detail == NotifyInferior)
- return TRUE;
- break;
+ return keyboard_interactively_grabbed();
case FocusIn:
case FocusOut:
/* I don't think this should ever happen with our event masks, but
}
}
-#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);
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 (group)
event_handle_group(group, e);
else if (client)
event_handle_client(client, e);
msgtype = e->xclient.message_type;
if (msgtype == prop_atoms.net_current_desktop) {
guint d = e->xclient.data.l[0];
- event_curtime = e->xclient.data.l[1];
- 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)
if (client_normal(client) && client_can_focus(client)) {
if (config_focus_delay) {
ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+
+ focus_delay_data.client = client;
+ focus_delay_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);
+ NULL, NULL);
+ } else {
+ focus_delay_data.client = client;
+ focus_delay_data.time = event_curtime;
+ focus_delay_func(NULL);
+ }
}
}
/* 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);
+ 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*/
break;
+ } else if (ce.xfocus.detail == NotifyPointerRoot ||
+ ce.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 (ce.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 {
/* Focus did move, so process the FocusIn event */
- ObEventData ed;
+ 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("Focus went to an unmanaged window 0x%x !\n",
- ce.xfocus.window);
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to an unmanaged window 0x%x !\n",
+ ce.xfocus.window);
focus_fallback(TRUE);
}
}
frame_adjust_state(client->frame);
break;
case OB_FRAME_CONTEXT_FRAME:
- if (config_focus_follow && config_focus_delay)
- ob_main_loop_timeout_remove_data(ob_main_loop,
- focus_delay_func,
- client, TRUE);
+ if (config_focus_follow && config_focus_delay &&
+ focus_delay_data.client == client)
+ {
+ event_halt_focus_delay();
+ }
break;
default:
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);
}
}
break;
case UnmapNotify:
- ob_debug("UnmapNotify for window 0x%x\n", client->window);
if (client->ignore_unmaps) {
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:
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:
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);
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);
}
- break;
case KeyPress:
if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
menu_frame_hide_all();
static gboolean focus_delay_func(gpointer data)
{
- ObClient *c = data;
-
- if (focus_client != c) {
- if (client_validate(c)) {
- client_focus(c);
- if (config_focus_raise)
- client_raise(c);
- }
+ Time old = event_curtime;
+ event_curtime = focus_delay_data.time;
+ if (focus_client != focus_delay_data.client) {
+ if (client_focus(focus_delay_data.client) && config_focus_raise)
+ client_raise(focus_delay_data.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);
+ if (focus_delay_data.client == client)
+ event_halt_focus_delay();
}
static void event_client_dest(ObClient *client, gpointer data)
void event_halt_focus_delay()
{
+ focus_delay_data.client = NULL;
ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
}
}
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);
+}