#include "grab.h"
#include "client.h"
#include "config.h"
+#include "group.h"
#include "focus_cycle.h"
#include "screen.h"
#include "prop.h"
static void push_to_top(ObClient *client)
{
+ ObClient *p;
+
+ /* if it is modal for a single window, then put that window at the top
+ of the focus order first, so it will be right after ours. the same is
+ done with stacking */
+ if (client->modal && (p = client_direct_parent(client)))
+ push_to_top(p);
+
focus_order = g_list_remove(focus_order, client);
focus_order = g_list_prepend(focus_order, client);
}
screen_install_colormap(focus_client, FALSE);
screen_install_colormap(client, TRUE);
- /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
- be used.
- */
- focus_cycle_stop();
+ /* in the middle of cycling..? kill it. */
+ focus_cycle_stop(focus_client);
+ focus_cycle_stop(client);
focus_client = client;
}
}
-static ObClient* focus_fallback_target(gboolean allow_refocus)
+static ObClient* focus_fallback_target(gboolean allow_refocus,
+ gboolean allow_pointer,
+ gboolean allow_omnipresent,
+ ObClient *old)
{
GList *it;
ObClient *c;
- ObClient *old = focus_client;
ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
- if (config_focus_follow && !config_focus_last)
+ if (allow_pointer && config_focus_follow)
if ((c = client_under_pointer()) &&
- (allow_refocus || c != old) &&
+ (allow_refocus || client_focus_target(c) != old) &&
(client_normal(c) &&
client_focus(c)))
{
return c;
}
- ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
- if (allow_refocus && old &&
- old->desktop == DESKTOP_ALL &&
- client_normal(old) &&
- client_focus(old))
- {
- ob_debug_type(OB_DEBUG_FOCUS, "found in omnipresentness\n");
- return old;
- }
-
-
ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order\n");
for (it = focus_order; it; it = g_list_next(it)) {
c = it->data;
/* fallback focus to a window if:
1. it is on the current desktop. this ignores omnipresent
- windows, which are problematic in their own rite.
- 2. it is a normal type window, don't fall back onto a dock or
- a splashscreen or a desktop window (save the desktop as a
- backup fallback though)
+ windows, which are problematic in their own rite, unless they are
+ specifically allowed
+ 2. it is a valid auto-focus target
+ 3. it is not shaded
*/
- if (c->desktop == screen_desktop &&
- client_normal(c) &&
- (allow_refocus || c != old) &&
+ if ((allow_omnipresent || c->desktop == screen_desktop) &&
+ focus_valid_target(c, TRUE, FALSE, FALSE, FALSE, FALSE) &&
+ !c->shaded &&
+ (allow_refocus || client_focus_target(c) != old) &&
client_focus(c))
{
ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
a splashscreen or a desktop window (save the desktop as a
backup fallback though)
*/
- if (c->type == OB_CLIENT_TYPE_DESKTOP &&
- (allow_refocus || c != old) &&
+ if (focus_valid_target(c, TRUE, FALSE, FALSE, FALSE, TRUE) &&
+ (allow_refocus || client_focus_target(c) != old) &&
client_focus(c))
{
ob_debug_type(OB_DEBUG_FOCUS, "found a desktop window\n");
return NULL;
}
-ObClient* focus_fallback(gboolean allow_refocus)
+ObClient* focus_fallback(gboolean allow_refocus, gboolean allow_pointer,
+ gboolean allow_omnipresent, gboolean focus_lost)
{
ObClient *new;
+ ObClient *old = focus_client;
/* unfocus any focused clients.. they can be focused by Pointer events
and such, and then when we try focus them, we won't get a FocusIn
event at all for them. */
- focus_nothing();
+ if (focus_lost)
+ focus_nothing();
- new = focus_fallback_target(allow_refocus);
+ new = focus_fallback_target(allow_refocus, allow_pointer,
+ allow_omnipresent, old);
+ /* get what was really focused */
+ if (new) new = client_focus_target(new);
return new;
}
-void focus_nothing()
+void focus_nothing(void)
{
/* Install our own colormap */
if (focus_client != NULL) {
screen_install_colormap(NULL, TRUE);
}
- /* Don't set focus_client to NULL here. It will be set to NULL when the
- FocusOut event comes. Otherwise, if we focus nothing and then focus the
- same window again, The focus code says nothing changed, but focus_client
- ends up being NULL anyways.
- focus_client = NULL;
- */
+ /* nothing is focused, update the colormap and _the root property_ */
+ focus_set_client(NULL);
/* if there is a grab going on, then we need to cancel it. if we move
focus during the grab, applications will get NotifyWhileGrabbed events
actions should not rely on being able to move focus during an
interactive grab.
*/
- if (keyboard_interactively_grabbed())
- keyboard_interactive_cancel();
+ event_cancel_all_key_grabs();
/* when nothing will be focused, send focus to the backup target */
XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
event_curtime);
}
+void focus_order_add_new(ObClient *c)
+{
+ if (c->iconic)
+ focus_order_to_top(c);
+ else {
+ g_assert(!g_list_find(focus_order, c));
+ /* if there are any iconic windows, put this above them in the order,
+ but if there are not, then put it under the currently focused one */
+ if (focus_order && ((ObClient*)focus_order->data)->iconic)
+ focus_order = g_list_insert(focus_order, c, 0);
+ else
+ focus_order = g_list_insert(focus_order, c, 1);
+ }
+
+ /* in the middle of cycling..? kill it. */
+ focus_cycle_stop(c);
+}
+
void focus_order_remove(ObClient *c)
{
focus_order = g_list_remove(focus_order, c);
+
+ /* in the middle of cycling..? kill it. */
+ focus_cycle_stop(c);
}
void focus_order_to_top(ObClient *c)
}
return NULL;
}
+
+/*! Returns if a focus target has valid group siblings that can be cycled
+ to in its place */
+static gboolean focus_target_has_siblings(ObClient *ft,
+ gboolean iconic_windows,
+ gboolean all_desktops)
+
+{
+ GSList *it;
+
+ if (!ft->group) return FALSE;
+
+ for (it = ft->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ /* check that it's not a helper window to avoid infinite recursion */
+ if (c != ft && c->type == OB_CLIENT_TYPE_NORMAL &&
+ focus_valid_target(c, TRUE, iconic_windows, all_desktops,
+ FALSE, FALSE))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+gboolean focus_valid_target(ObClient *ft,
+ gboolean helper_windows,
+ gboolean iconic_windows,
+ gboolean all_desktops,
+ gboolean dock_windows,
+ gboolean desktop_windows)
+{
+ gboolean ok = FALSE;
+
+ /* it's on this desktop unless you want all desktops.
+
+ do this check first because it will usually filter out the most
+ windows */
+ ok = (all_desktops || ft->desktop == screen_desktop ||
+ ft->desktop == DESKTOP_ALL);
+
+ /* the window can receive focus somehow */
+ ok = ok && (ft->can_focus || ft->focus_notify);
+
+ /* the window is not iconic, or we're allowed to go to iconic ones */
+ ok = ok && (iconic_windows || !ft->iconic);
+
+ /* it's the right type of window */
+ if (dock_windows || desktop_windows)
+ ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
+ (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
+ /* modal windows are important and can always get focus if they are
+ visible and stuff, so don't change 'ok' based on their type */
+ else if (!ft->modal)
+ /* normal non-helper windows are valid targets */
+ ok = ok &&
+ ((client_normal(ft) && !client_helper(ft))
+ ||
+ /* helper windows are valid targets if... */
+ (client_helper(ft) &&
+ /* ...a window in its group already has focus and we want to
+ include helper windows ... */
+ ((focus_client && ft->group == focus_client->group &&
+ helper_windows) ||
+ /* ... or if there are no other windows in its group
+ that can be focused instead */
+ !focus_target_has_siblings(ft, iconic_windows, all_desktops))));
+
+ /* it's not set to skip the taskbar (but this only applies to normal typed
+ windows, and is overridden if the window is modal) */
+ ok = ok && (ft->type != OB_CLIENT_TYPE_NORMAL ||
+ ft->modal ||
+ !ft->skip_taskbar);
+
+ /* it's not going to just send focus off somewhere else (modal window),
+ unless that modal window is not one of our valid targets, then let
+ you choose this window and bring the modal one here */
+ {
+ ObClient *cft = client_focus_target(ft);
+ ok = ok && (ft == cft || !focus_valid_target(cft,
+ TRUE,
+ iconic_windows,
+ all_desktops,
+ dock_windows,
+ desktop_windows));
+ }
+
+ return ok;
+}
+