#include "config.h"
#include "screen.h"
#include "frame.h"
+#include "grab.h"
#include "menu.h"
#include "menuframe.h"
#include "keyboard.h"
#include "mainloop.h"
#include "framerender.h"
#include "focus.h"
+#include "focus_cycle.h"
#include "moveresize.h"
#include "group.h"
#include "stacking.h"
static void event_handle_client(ObClient *c, XEvent *e);
static void event_handle_user_time_window_clients(GSList *l, XEvent *e);
static void event_handle_user_input(ObClient *client, XEvent *e);
+static gboolean is_enter_focus_event_ignored(XEvent *e);
static void focus_delay_dest(gpointer data);
static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
static guint ignore_enter_focus = 0;
static gboolean menu_can_hide;
static gboolean focus_left_screen = FALSE;
-/*! This variable is used for focus fallback. If we fallback to a window, we
- set this to the window. And when focus goes somewhere after that, it will
- be set to NULL. If between falling back to that window and something
- getting focused, the window gets unmanaged, then if there are no incoming
- FocusIn events, we fallback again because focus has just gotten itself lost.
- */
-static ObClient *focus_tried = NULL;
#ifdef USE_SM
static void ice_handler(gint fd, gpointer conn)
IceAddConnectionWatch(ice_watch, NULL);
#endif
- client_add_destructor(focus_delay_client_dest, NULL);
+ client_add_destroy_notify(focus_delay_client_dest, NULL);
}
void event_shutdown(gboolean reconfig)
IceRemoveConnectionWatch(ice_watch, NULL);
#endif
- client_remove_destructor(focus_delay_client_dest);
+ client_remove_destroy_notify(focus_delay_client_dest);
}
static Window event_get_window(XEvent *e)
return FALSE;
}
+ /* It was on a client, was it a valid one?
+ It's possible to get a FocusIn event for a client that was managed
+ but has disappeared.
+ */
+ if (in_client_only) {
+ ObWindow *w = g_hash_table_lookup(window_map, &e->xfocus.window);
+ if (!w || !WINDOW_IS_CLIENT(w))
+ return FALSE;
+ }
+ else {
+ /* This means focus reverted to parent from the client (this
+ happens often during iconify animation) */
+ if (detail == NotifyInferior)
+ return TRUE;
+ }
+
/* 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 moved to the frame window */
- if (detail == NotifyInferior && !in_client_only)
- return TRUE;
/* Otherwise.. */
return FALSE;
/* This means focus was taken by a keyboard/mouse grab. */
if (mode == NotifyGrab)
return FALSE;
+ /* This means focus was grabbed on a window and it was released. */
+ if (mode == NotifyUngrab)
+ return FALSE;
/* Focus left the root window revertedto state */
if (win == RootWindow(ob_display, ob_screen))
/* This means focus moved from one client to another */
if (detail == NotifyNonlinearVirtual)
return TRUE;
- /* This means focus had moved to our frame window and now moved off */
- if (detail == NotifyNonlinear)
- return TRUE;
/* Otherwise.. */
return FALSE;
}
}
-static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
+static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
{
return e->type == FocusIn && wanted_focusevent(e, FALSE);
}
-static Bool look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
+static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
{
return e->type == FocusIn && wanted_focusevent(e, TRUE);
}
case NotifyDetailNone: detailstr="NotifyDetailNone"; break;
}
+ if (mode == NotifyGrab || mode == NotifyUngrab)
+ return;
+
g_assert(modestr);
g_assert(detailstr);
ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s\n",
e->xfocus.detail == NotifyInferior)
{
XEvent ce;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Focus went to pointer root/none or to our frame "
- "window\n");
+
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to pointer root/none or"
+ " the frame window\n");
/* If another FocusIn is in the queue then don't fallback yet. This
fixes the fun case of:
But if the other focus in is something like PointerRoot then we
still want to fall back.
*/
- if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
+ if (XCheckIfEvent(ob_display, &ce, event_look_for_focusin_client,
+ NULL))
+ {
XPutBackEvent(ob_display, &ce);
ob_debug_type(OB_DEBUG_FOCUS,
" but another FocusIn is coming\n");
} else {
- /* Focus has been reverted to the root window, nothing, or to
- our frame window.
+ /* Focus has been reverted.
FocusOut events come after UnmapNotify, so we don't need to
worry about focusing an invalid window
*/
- /* In this case we know focus is in our screen */
- if (e->xfocus.detail == NotifyInferior)
- focus_left_screen = FALSE;
-
if (!focus_left_screen)
- focus_tried = focus_fallback(TRUE);
+ focus_fallback(TRUE, FALSE);
}
- } else if (client && client != focus_client) {
+ }
+ else if (!client)
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to a window that is already gone\n");
+
+ /* If you send focus to a window and then it disappears, you can
+ get the FocusIn for it, after it is unmanaged.
+ Just wait for the next FocusOut/FocusIn pair. */
+ }
+ else if (client != focus_client) {
focus_left_screen = FALSE;
frame_adjust_focus(client->frame, TRUE);
focus_set_client(client);
client_calc_layer(client);
client_bring_helper_windows(client);
-
- focus_tried = NULL; /* focus isn't "trying" to go anywhere now */
}
} else if (e->type == FocusOut) {
- gboolean nomove = FALSE;
XEvent ce;
/* Look for the followup FocusIn */
- if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
+ if (!XCheckIfEvent(ob_display, &ce, event_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. */
Window win, root;
xerror_set_ignore(FALSE);
/* nothing is focused */
focus_set_client(NULL);
- } else if (ce.xany.window == e->xany.window) {
- ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n");
- /* If focus didn't actually move anywhere, there is nothing to do*/
- nomove = TRUE;
} else {
- /* Focus did move, so process the FocusIn event */
+ /* Focus moved, so process the FocusIn event */
ObEventData ed = { .ignored = FALSE };
event_process(&ce, &ed);
if (ed.ignored) {
ob_debug_type(OB_DEBUG_FOCUS,
"Focus went to an unmanaged window 0x%x !\n",
ce.xfocus.window);
- focus_tried = focus_fallback(TRUE);
+ focus_fallback(TRUE, FALSE);
}
}
- if (client && !nomove) {
+ if (client && client != focus_client) {
frame_adjust_focus(client->frame, FALSE);
- /* focus_set_client has already been called for sure */
+ /* focus_set_client(NULL) has already been called in this
+ section or by focus_fallback */
client_calc_layer(client);
}
} else if (timewinclients)
}
} else if (msgtype == prop_atoms.net_number_of_desktops) {
guint d = e->xclient.data.l[0];
- if (d > 0)
+ if (d > 0 && d <= 1000)
screen_set_num_desktops(d);
} else if (msgtype == prop_atoms.net_showing_desktop) {
screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
- } else if (msgtype == prop_atoms.openbox_control) {
+ } else if (msgtype == prop_atoms.ob_control) {
if (e->xclient.data.l[0] == 1)
ob_reconfigure();
else if (e->xclient.data.l[0] == 2)
{
/* use where the press occured */
con = frame_context(client, e->xbutton.window, px, py);
- con = mouse_button_frame_context(con, e->xbutton.button);
+ con = mouse_button_frame_context(con, e->xbutton.button,
+ e->xbutton.state);
if (e->type == ButtonRelease && e->xbutton.button == pb)
pb = 0, px = py = -1;
e->xmotion.x, e->xmotion.y);
switch (con) {
case OB_FRAME_CONTEXT_TITLEBAR:
+ case OB_FRAME_CONTEXT_TLCORNER:
+ case OB_FRAME_CONTEXT_TRCORNER:
/* we've left the button area inside the titlebar */
if (client->frame->max_hover || client->frame->desk_hover ||
client->frame->shade_hover || client->frame->iconify_hover ||
con = frame_context(client, e->xcrossing.window,
e->xcrossing.x, e->xcrossing.y);
switch (con) {
+ case OB_FRAME_CONTEXT_TITLEBAR:
+ case OB_FRAME_CONTEXT_TLCORNER:
+ case OB_FRAME_CONTEXT_TRCORNER:
+ /* we've left the button area inside the titlebar */
+ if (client->frame->max_hover || client->frame->desk_hover ||
+ client->frame->shade_hover || client->frame->iconify_hover ||
+ client->frame->close_hover)
+ {
+ client->frame->max_hover = FALSE;
+ client->frame->desk_hover = FALSE;
+ client->frame->shade_hover = FALSE;
+ client->frame->iconify_hover = FALSE;
+ client->frame->close_hover = FALSE;
+ frame_adjust_state(client->frame);
+ }
+ break;
case OB_FRAME_CONTEXT_MAXIMIZE:
client->frame->max_hover = FALSE;
frame_adjust_state(client->frame);
corresponding enter events. Pretend like the animating window
doesn't even exist..! */
if (frame_iconify_animating(client->frame))
- event_ignore_queued_enters();
+ event_ignore_all_queued_enters();
ob_debug_type(OB_DEBUG_FOCUS,
"%sNotify mode %d detail %d on %lx\n",
break;
case EnterNotify:
{
- gboolean nofocus = FALSE;
-
- if (ignore_enter_focus) {
- ignore_enter_focus--;
- nofocus = TRUE;
- }
-
con = frame_context(client, e->xcrossing.window,
e->xcrossing.x, e->xcrossing.y);
switch (con) {
if (e->xcrossing.mode == NotifyGrab ||
e->xcrossing.mode == NotifyUngrab ||
/*ignore enters when we're already in the window */
- e->xcrossing.detail == NotifyInferior)
+ e->xcrossing.detail == NotifyInferior ||
+ is_enter_focus_event_ignored(e))
{
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 {
+ }
+ else {
ob_debug_type(OB_DEBUG_FOCUS,
"%sNotify mode %d detail %d on %lx, "
- "focusing window: %d\n",
+ "focusing window\n",
(e->type == EnterNotify ? "Enter" : "Leave"),
e->xcrossing.mode,
- e->xcrossing.detail, (client?client->window:0),
- !nofocus);
- if (!nofocus && config_focus_follow)
+ e->xcrossing.detail, (client?client->window:0));
+ if (config_focus_follow)
event_enter_client(client);
}
break;
also you can't compress stacking events
*/
- gint x, y, w, h;
-
- /* if nothing is changed, then a configurenotify is needed */
- gboolean config = TRUE;
+ gint x, y, w, h, b;
+ gboolean move = FALSE;
+ gboolean resize = FALSE;
+ gboolean border = FALSE;
- x = client->area.x;
- y = client->area.y;
- w = client->area.width;
- h = client->area.height;
+ /* get the current area */
+ RECT_TO_DIMS(client->area, x, y, w, h);
+ b = client->border_width;
ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
screen_desktop, client->wmstate, client->frame->visible);
if (e->xconfigurerequest.value_mask & CWBorderWidth)
if (client->border_width != e->xconfigurerequest.border_width) {
- client->border_width = e->xconfigurerequest.border_width;
- /* if only the border width is changing, then it's not needed*/
- config = FALSE;
+ b = e->xconfigurerequest.border_width;
+ border = TRUE;
}
stacking_restack_request(client, sibling,
e->xconfigurerequest.detail, TRUE);
- /* if a stacking change is requested then it is needed */
- config = TRUE;
- }
-
- /* don't allow clients to move shaded windows (fvwm does this) */
- if (client->shaded && (e->xconfigurerequest.value_mask & CWX ||
- e->xconfigurerequest.value_mask & CWY))
- {
- e->xconfigurerequest.value_mask &= ~CWX;
- e->xconfigurerequest.value_mask &= ~CWY;
-
- /* if the client tried to move and we aren't letting it then a
- synthetic event is needed */
- config = TRUE;
+ /* if a stacking change moves the window without resizing */
+ move = TRUE;
}
if (e->xconfigurerequest.value_mask & CWX ||
e->xconfigurerequest.value_mask & CWWidth ||
e->xconfigurerequest.value_mask & CWHeight)
{
- if (e->xconfigurerequest.value_mask & CWX)
- x = e->xconfigurerequest.x;
- if (e->xconfigurerequest.value_mask & CWY)
- y = e->xconfigurerequest.y;
- if (e->xconfigurerequest.value_mask & CWWidth)
+ if (e->xconfigurerequest.value_mask & CWX) {
+ /* don't allow clients to move shaded windows (fvwm does this)
+ */
+ if (!client->shaded)
+ x = e->xconfigurerequest.x;
+ move = TRUE;
+ }
+ if (e->xconfigurerequest.value_mask & CWY) {
+ /* don't allow clients to move shaded windows (fvwm does this)
+ */
+ if (!client->shaded)
+ y = e->xconfigurerequest.y;
+ move = TRUE;
+ }
+
+ if (e->xconfigurerequest.value_mask & CWWidth) {
w = e->xconfigurerequest.width;
- if (e->xconfigurerequest.value_mask & CWHeight)
+ resize = TRUE;
+
+ /* if x was not given, then use gravity to figure out the new
+ x. the reference point should not be moved */
+ if (!(e->xconfigurerequest.value_mask & CWX))
+ client_gravity_resize_w(client, &x, client->area.width, w);
+ }
+ if (e->xconfigurerequest.value_mask & CWHeight) {
h = e->xconfigurerequest.height;
+ resize = TRUE;
- /* if a new position or size is requested, then a configure is
- needed */
- config = TRUE;
+ /* if y was not given, then use gravity to figure out the new
+ y. the reference point should not be moved */
+ if (!(e->xconfigurerequest.value_mask & CWY))
+ client_gravity_resize_h(client, &y, client->area.height,h);
+ }
}
- ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
+ ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
+ "move %d resize %d\n",
e->xconfigurerequest.value_mask & CWX, x,
e->xconfigurerequest.value_mask & CWY, y,
e->xconfigurerequest.value_mask & CWWidth, w,
- e->xconfigurerequest.value_mask & CWHeight, h);
+ e->xconfigurerequest.value_mask & CWHeight, h,
+ move, resize);
/* check for broken apps moving to their root position
desktop. eg. open amarok window on desktop 1, switch to desktop
2, click amarok tray icon. it will move by its decoration size.
*/
- if (x != client->area.x &&
+ if (move && !resize &&
+ x != client->area.x &&
x == (client->frame->area.x + client->frame->size.left -
(gint)client->border_width) &&
y != client->area.y &&
/* don't move it */
x = client->area.x;
y = client->area.y;
+
+ /* they still requested a move, so don't change whether a
+ notify is sent or not */
}
- if (config) {
- client_find_onscreen(client, &x, &y, w, h, FALSE);
- client_configure_full(client, x, y, w, h, FALSE, TRUE);
+ if (move || resize || border) {
+ gint lw,lh;
+
+ if (move || resize) {
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+ client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
+ }
+ /* if they requested something that moves the window, or if
+ the window is actually being changed then configure it and
+ send a configure notify to them */
+ if (move || !RECT_EQUAL_DIMS(client->area, x, y, w, h) ||
+ border)
+ {
+ ob_debug("Doing configure\n");
+ client_configure(client, x, y, w, h, b, FALSE, TRUE);
+ }
+
+ /* ignore enter events caused by these like ob actions do */
+ event_ignore_all_queued_enters();
}
break;
}
client->window, e->xunmap.event, e->xunmap.from_configure,
client->ignore_unmaps);
client_unmanage(client);
-
- /* we were trying to focus this window but it's gone */
- if (client == focus_tried) {
- ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
- "is being unmanaged:\n");
- if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
- XPutBackEvent(ob_display, &ce);
- ob_debug_type(OB_DEBUG_FOCUS,
- " but another FocusIn is coming\n");
- } else {
- ob_debug_type(OB_DEBUG_FOCUS,
- " so falling back focus again.\n");
- focus_tried = focus_fallback(TRUE);
- }
- }
break;
case DestroyNotify:
ob_debug("DestroyNotify for window 0x%x\n", client->window);
client_unmanage(client);
-
- /* we were trying to focus this window but it's gone */
- if (client == focus_tried) {
- ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
- "is being unmanaged:\n");
- if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
- XPutBackEvent(ob_display, &ce);
- ob_debug_type(OB_DEBUG_FOCUS,
- " but another FocusIn is coming\n");
- } else {
- ob_debug_type(OB_DEBUG_FOCUS,
- " so falling back focus again.\n");
- focus_tried = focus_fallback(TRUE);
- }
- }
break;
case ReparentNotify:
/* this is when the client is first taken captive in the frame */
ob_debug("ReparentNotify for window 0x%x\n", client->window);
client_unmanage(client);
-
- /* we were trying to focus this window but it's gone */
- if (client == focus_tried) {
- ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
- "is being unmanaged:\n");
- if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
- XPutBackEvent(ob_display, &ce);
- ob_debug_type(OB_DEBUG_FOCUS,
- " but another FocusIn is coming\n");
- } else {
- ob_debug_type(OB_DEBUG_FOCUS,
- " so falling back focus again.\n");
- focus_tried = focus_fallback(TRUE);
- }
- }
break;
case MapRequest:
ob_debug("MapRequest for 0x%lx\n", client->window);
client->window);
client_set_state(client, e->xclient.data.l[0],
e->xclient.data.l[1], e->xclient.data.l[2]);
+
+ /* ignore enter events caused by these like ob actions do */
+ event_ignore_all_queued_enters();
} else if (msgtype == prop_atoms.net_close_window) {
ob_debug("net_close_window for 0x%lx\n", client->window);
client_close(client);
(e->xclient.data.l[0] == 1 ? "application" :
(e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
/* XXX make use of data.l[2] !? */
- event_curtime = e->xclient.data.l[1];
- if (event_curtime == 0)
+ if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
+ event_curtime = e->xclient.data.l[1];
+ if (event_curtime == 0)
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_ACTIVE_WINDOW message for window %s is"
+ " missing a timestamp\n", client->title);
+ } else
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_ACTIVE_WINDOW message for window %s is "
- "missing a timestamp\n", client->title);
+ "missing source indication\n");
client_activate(client, FALSE,
(e->xclient.data.l[0] == 0 ||
e->xclient.data.l[0] == 2));
prop_atoms.net_wm_moveresize_cancel)
moveresize_end(TRUE);
} else if (msgtype == prop_atoms.net_moveresize_window) {
- gint grav, x, y, w, h;
+ gint ograv, x, y, w, h;
+
+ ograv = client->gravity;
if (e->xclient.data.l[0] & 0xff)
- grav = e->xclient.data.l[0] & 0xff;
- else
- grav = client->gravity;
+ client->gravity = e->xclient.data.l[0] & 0xff;
if (e->xclient.data.l[0] & 1 << 8)
x = e->xclient.data.l[1];
y = e->xclient.data.l[2];
else
y = client->area.y;
- if (e->xclient.data.l[0] & 1 << 10)
+
+ if (e->xclient.data.l[0] & 1 << 10) {
w = e->xclient.data.l[3];
+
+ /* if x was not given, then use gravity to figure out the new
+ x. the reference point should not be moved */
+ if (!(e->xclient.data.l[0] & 1 << 8))
+ client_gravity_resize_w(client, &x, client->area.width, w);
+ }
else
w = client->area.width;
- if (e->xclient.data.l[0] & 1 << 11)
+
+ if (e->xclient.data.l[0] & 1 << 11) {
h = e->xclient.data.l[4];
+
+ /* if y was not given, then use gravity to figure out the new
+ y. the reference point should not be moved */
+ if (!(e->xclient.data.l[0] & 1 << 9))
+ client_gravity_resize_h(client, &y, client->area.height,h);
+ }
else
h = client->area.height;
- ob_debug("MOVERESIZE x %d %d y %d %d\n",
+ ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
e->xclient.data.l[0] & 1 << 8, x,
- e->xclient.data.l[0] & 1 << 9, y);
- client_convert_gravity(client, grav, &x, &y, w, h);
+ e->xclient.data.l[0] & 1 << 9, y,
+ client->gravity);
+
client_find_onscreen(client, &x, &y, w, h, FALSE);
- client_configure(client, x, y, w, h, FALSE, TRUE);
+
+ client_configure(client, x, y, w, h, client->border_width,
+ FALSE, TRUE);
+
+ client->gravity = ograv;
+
+ /* ignore enter events caused by these like ob actions do */
+ event_ignore_all_queued_enters();
} else if (msgtype == prop_atoms.net_restack_window) {
if (e->xclient.data.l[0] != 2) {
ob_debug_type(OB_DEBUG_APP_BUGS,
} else {
ObClient *sibling = NULL;
if (e->xclient.data.l[1]) {
- ObWindow *win = g_hash_table_lookup(window_map,
- &e->xclient.data.l[1]);
+ ObWindow *win = g_hash_table_lookup
+ (window_map, &e->xclient.data.l[1]);
if (WINDOW_IS_CLIENT(win) &&
WINDOW_AS_CLIENT(win) != client)
{
e->xclient.data.l[2], FALSE);
/* send a synthetic ConfigureNotify, cuz this is supposed
to be like a ConfigureRequest. */
- client_configure_full(client, client->area.x,
- client->area.y,
- client->area.width,
- client->area.height,
- FALSE, TRUE);
+ client_reconfigure(client);
} else
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_RESTACK_WINDOW sent for window %s "
client_update_sync_request_counter(client);
}
#endif
+ break;
case ColormapNotify:
client_update_colormap(client, e->xcolormap.colormap);
break;
ret = FALSE;
else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
- /* Escape closes the active menu */
- menu_frame_hide(frame);
+ /* Escape goes to the parent menu or closes the last one */
+ if (frame->parent)
+ menu_frame_select(frame, NULL, TRUE);
+ else
+ menu_frame_hide_all();
}
else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
Control-Enter runs it without closing the menu. */
if (frame->child)
menu_frame_select_next(frame->child);
- else
+ else if (frame->selected)
menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
}
menu_frame_select_next(frame);
}
- /* keyboard accelerator shortcuts. */
- else if (ev->xkey.state == 0 &&
+ /* keyboard accelerator shortcuts. (allow controlmask) */
+ else if ((ev->xkey.state & ~ControlMask) == 0 &&
/* was it a valid key? */
unikey != 0 &&
/* don't bother if the menu is empty. */
}
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)) &&
(f = find_active_menu()) && f->selected == e &&
e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
}
-void event_ignore_queued_enters()
+static Bool event_look_for_enters(Display *d, XEvent *e, XPointer arg)
{
- GSList *saved = NULL, *it;
- XEvent *e;
-
- XSync(ob_display, FALSE);
+ if (e->type == EnterNotify &&
+ /* these types aren't used for focusing */
+ !(e->xcrossing.mode == NotifyGrab ||
+ e->xcrossing.mode == NotifyUngrab ||
+ e->xcrossing.detail == NotifyInferior))
+ {
+ ObWindow *win;
- /* count the events */
- while (TRUE) {
- e = g_new(XEvent, 1);
- if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
- ObWindow *win;
-
- win = g_hash_table_lookup(window_map, &e->xany.window);
- if (win && WINDOW_IS_CLIENT(win))
- ++ignore_enter_focus;
-
- saved = g_slist_append(saved, e);
- } else {
- g_free(e);
- break;
- }
+ /* found an enter for that leave, ignore it if it's going to
+ another window */
+ win = g_hash_table_lookup(window_map, &e->xany.window);
+ if (win && WINDOW_IS_CLIENT(win))
+ ++ignore_enter_focus;
}
- /* put the events back */
- for (it = saved; it; it = g_slist_next(it)) {
- XPutBackEvent(ob_display, it->data);
- g_free(it->data);
+ return False; /* don't disrupt the queue order, just count them */
+}
+
+void event_ignore_all_queued_enters()
+{
+ XEvent e;
+
+ XSync(ob_display, FALSE);
+
+ /* count the events without disrupting them */
+ ignore_enter_focus = 0;
+ XCheckIfEvent(ob_display, &e, event_look_for_enters, NULL);
+}
+
+static gboolean is_enter_focus_event_ignored(XEvent *e)
+{
+ g_assert(e->type == EnterNotify &&
+ !(e->xcrossing.mode == NotifyGrab ||
+ e->xcrossing.mode == NotifyUngrab ||
+ e->xcrossing.detail == NotifyInferior));
+
+ ob_debug_type(OB_DEBUG_FOCUS, "# enters ignored: %d\n",
+ ignore_enter_focus);
+
+ if (ignore_enter_focus) {
+ --ignore_enter_focus;
+ return TRUE;
}
- g_slist_free(saved);
+ return FALSE;
+}
+
+void event_cancel_all_key_grabs()
+{
+ if (keyboard_interactively_grabbed())
+ keyboard_interactive_cancel();
+ else if (menu_frame_visible)
+ menu_frame_hide_all();
+ else if (grab_on_keyboard())
+ ungrab_keyboard();
+ else
+ /* If we don't have the keyboard grabbed, then ungrab it with
+ XUngrabKeyboard, so that there is not a passive grab left
+ on from the KeyPress. If the grab is left on, and focus
+ moves during that time, it will be NotifyWhileGrabbed, and
+ applications like to ignore those! */
+ if (!keyboard_interactively_grabbed())
+ XUngrabKeyboard(ob_display, CurrentTime);
+
}
gboolean event_time_after(Time t1, Time t2)