#include <glib.h>
#include <assert.h>
-ObClient *focus_client, *focus_hilite;
-GList **focus_order; /* these lists are created when screen_startup
- sets the number of desktops */
-ObClient *focus_cycle_target;
+ObClient *focus_client = NULL;
+GList *focus_order = NULL;
+ObClient *focus_cycle_target = NULL;
struct {
InternalWindow top;
be used
*/
if (focus_cycle_target == client)
- focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
+ focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
}
static Window createWindow(Window parent, gulong mask,
client_add_destructor(focus_cycle_destructor, NULL);
/* start with nothing focused */
- focus_set_client(NULL);
+ focus_nothing();
focus_indicator.top.obwin.type = Window_Internal;
focus_indicator.left.obwin.type = Window_Internal;
void focus_shutdown(gboolean reconfig)
{
- guint i;
-
icon_popup_free(focus_cycle_popup);
if (!reconfig) {
client_remove_destructor(focus_cycle_destructor);
- for (i = 0; i < screen_num_desktops; ++i)
- g_list_free(focus_order[i]);
- g_free(focus_order);
-
/* reset focus to root */
XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
static void push_to_top(ObClient *client)
{
- guint desktop;
-
- desktop = client->desktop;
- if (desktop == DESKTOP_ALL) desktop = screen_desktop;
- focus_order[desktop] = g_list_remove(focus_order[desktop], client);
- focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
+ focus_order = g_list_remove(focus_order, client);
+ focus_order = g_list_prepend(focus_order, client);
}
void focus_set_client(ObClient *client)
{
Window active;
- ObClient *old;
-#ifdef DEBUG_FOCUS
- ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
-#endif
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "focus_set_client 0x%lx\n", client ? client->window : 0);
/* uninstall the old colormap, and install the new one */
screen_install_colormap(focus_client, FALSE);
screen_install_colormap(client, TRUE);
- if (client == NULL) {
-#ifdef DEBUG_FOCUS
- ob_debug("actively focusing NONWINDOW\n");
-#endif
- /* when nothing will be focused, send focus to the backup target */
- XSetInputFocus(ob_display, screen_support_win, RevertToNone,
- event_curtime);
- XSync(ob_display, FALSE);
- }
-
/* in the middle of cycling..? kill it. CurrentTime is fine, time won't
be used.
*/
if (focus_cycle_target)
- focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime);
+ focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
- old = focus_client;
focus_client = client;
- /* move to the top of the list */
- if (client != NULL)
+ if (client != NULL) {
+ /* move to the top of the list */
push_to_top(client);
+ /* remove hiliting from the window when it gets focused */
+ client_hilite(client, FALSE);
+ }
/* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
if (ob_state() != OB_STATE_EXITING) {
active = client ? client->window : None;
PROP_SET32(RootWindow(ob_display, ob_screen),
net_active_window, window, active);
-
- /* remove hiliting from the window when it gets focused */
- if (client != NULL)
- client_hilite(client, FALSE);
}
}
-/* finds the first transient that isn't 'skip' and ensure's that client_normal
- is true for it */
-static ObClient *find_transient_recursive(ObClient *c, ObClient *top,
- ObClient *skip)
-{
- GSList *it;
- ObClient *ret;
-
- for (it = c->transients; it; it = g_slist_next(it)) {
- if (it->data == top) return NULL;
- ret = find_transient_recursive(it->data, top, skip);
- if (ret && ret != skip && client_normal(ret)) return ret;
- if (it->data != skip && client_normal(it->data)) return it->data;
- }
- return NULL;
-}
-
-static ObClient* focus_fallback_transient(ObClient *top, ObClient *old)
-{
- ObClient *target = find_transient_recursive(top, top, old);
- if (!target) {
- /* make sure client_normal is true always */
- if (!client_normal(top))
- return NULL;
- target = top; /* no transient, keep the top */
- }
- if (client_can_focus(target))
- return target;
- else
- return NULL;
-}
-
-ObClient* focus_fallback_target(ObFocusFallbackType type)
+ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
{
GList *it;
- ObClient *old;
ObClient *target = NULL;
+ ObClient *desktop = NULL;
- old = focus_client;
-
- if ((type == OB_FOCUS_FALLBACK_UNFOCUSING
- || type == OB_FOCUS_FALLBACK_CLOSED) && old) {
- if (old->transient_for) {
- gboolean trans = FALSE;
-
- if (!config_focus_follow || config_focus_last)
- trans = TRUE;
- else if ((target = client_under_pointer()) &&
- client_search_transient
- (client_search_top_parent(target), old))
- trans = TRUE;
-
- /* try for transient relations */
- if (trans) {
- if (old->transient_for == OB_TRAN_GROUP) {
- for (it = focus_order[screen_desktop]; it;
- it = g_list_next(it))
- {
- GSList *sit;
-
- for (sit = old->group->members; sit;
- sit = g_slist_next(sit))
- {
- if (sit->data == it->data)
- if ((target =
- focus_fallback_transient(sit->data, old)))
- {
- ob_debug("found in transient #1\n");
- return target;
- }
- }
- }
- } else {
- if ((target =
- focus_fallback_transient(old->transient_for, old)))
- {
- ob_debug("found in transient #2\n");
- return target;
- }
- }
- }
- }
- }
-
- ob_debug("trying pointer stuff\n");
- if (config_focus_follow &&
- (type == OB_FOCUS_FALLBACK_UNFOCUSING || !config_focus_last))
+ ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
+ if (config_focus_follow && !config_focus_last)
{
if ((target = client_under_pointer()))
- if (client_normal(target) && client_can_focus(target)) {
- ob_debug("found in pointer stuff\n");
- return target;
- }
+ if (allow_refocus || target != old)
+ if (client_normal(target) && client_can_focus(target)) {
+ ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
+ return target;
+ }
}
#if 0
}
#endif
- ob_debug("trying the focus order\n");
- for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
- if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
- if (client_normal(it->data) && client_can_focus(it->data)) {
- ob_debug("found in focus order\n");
- return it->data;
+ ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
+ if (allow_refocus && old && old->desktop == DESKTOP_ALL)
+ return old;
+
+
+ ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order\n");
+ for (it = focus_order; it; it = g_list_next(it))
+ if (allow_refocus || it->data != old) {
+ ObClient *c = it->data;
+ /* fallback focus to a window if:
+ 1. it is actually focusable, cuz if it's not then we're sending
+ focus off to nothing
+ 2. it is validated. if the window is about to disappear, then
+ don't try focus it.
+ 3. it is visible on the current desktop. this ignores
+ omnipresent windows, which are problematic in their own rite.
+ 4. it's not iconic
+ 5. 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)
+ */
+ if (client_can_focus(c) && c->desktop == screen_desktop &&
+ !c->iconic)
+ {
+ if (client_normal(c)) {
+ ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
+ return it->data;
+ } else if (c->type == OB_CLIENT_TYPE_DESKTOP && !desktop)
+ desktop = c;
}
+ }
- /* XXX fallback to the "desktop window" if one exists ?
- could store it while going through all the windows in the loop right
- above this..
+ /* as a last resort fallback to the desktop window if there is one.
+ (if there's more than one, then the one most recently focused.)
*/
-
- return NULL;
+ return desktop;
}
-void focus_fallback(ObFocusFallbackType type)
+void focus_fallback(gboolean allow_refocus)
{
ObClient *new;
+ ObClient *old = focus_client;
/* unfocus any focused clients.. they can be focused by Pointer events
and such, and then when I try focus them, I won't get a FocusIn event
at all for them.
*/
- focus_set_client(NULL);
+ focus_nothing();
- if ((new = focus_fallback_target(type)))
+ if ((new = focus_fallback_target(allow_refocus, old)))
client_focus(new);
}
+void focus_nothing()
+{
+ /* Install our own colormap */
+ if (focus_client != NULL) {
+ screen_install_colormap(focus_client, FALSE);
+ screen_install_colormap(NULL, TRUE);
+ }
+
+ focus_client = NULL;
+
+ /* when nothing will be focused, send focus to the backup target */
+ XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
+ event_curtime);
+}
+
static void popup_cycle(ObClient *c, gboolean show)
{
if (!show) {
void focus_cycle_draw_indicator()
{
if (!focus_cycle_target) {
+ XEvent e;
+
XUnmapWindow(ob_display, focus_indicator.top.win);
XUnmapWindow(ob_display, focus_indicator.left.win);
XUnmapWindow(ob_display, focus_indicator.right.win);
XUnmapWindow(ob_display, focus_indicator.bottom.win);
+
+ /* kill enter events cause by this unmapping */
+ XSync(ob_display, FALSE);
+ while (XCheckTypedEvent(ob_display, EnterNotify, &e));
} else {
/*
if (focus_cycle_target)
}
void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
- gboolean dialog, gboolean done, gboolean cancel, Time time)
+ gboolean dialog, gboolean done, gboolean cancel)
{
static ObClient *first = NULL;
static ObClient *t = NULL;
} else if (done)
goto done_cycle;
- if (!focus_order[screen_desktop])
+ if (!focus_order)
goto done_cycle;
if (!first) first = focus_client;
if (linear) list = client_list;
- else list = focus_order[screen_desktop];
+ else list = focus_order;
} else {
- if (!focus_order[screen_desktop])
+ if (!focus_order)
goto done_cycle;
list = client_list;
}
done_cycle:
if (done && focus_cycle_target)
- client_activate(focus_cycle_target, FALSE, TRUE, time);
+ client_activate(focus_cycle_target, FALSE, TRUE);
t = NULL;
first = NULL;
}
void focus_directional_cycle(ObDirection dir, gboolean interactive,
- gboolean dialog, gboolean done, gboolean cancel,
- Time time)
+ gboolean dialog, gboolean done, gboolean cancel)
{
static ObClient *first = NULL;
ObClient *ft = NULL;
} else if (done)
goto done_cycle;
- if (!focus_order[screen_desktop])
+ if (!focus_order)
goto done_cycle;
if (!first) first = focus_client;
else {
GList *it;
- for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
+ for (it = focus_order; it; it = g_list_next(it))
if (valid_focus_target(it->data))
ft = it->data;
}
done_cycle:
if (done && focus_cycle_target)
- client_activate(focus_cycle_target, FALSE, TRUE, time);
+ client_activate(focus_cycle_target, FALSE, TRUE);
first = NULL;
focus_cycle_target = NULL;
void focus_order_add_new(ObClient *c)
{
- guint d, i;
-
if (c->iconic)
focus_order_to_top(c);
else {
- d = c->desktop;
- if (d == DESKTOP_ALL) {
- for (i = 0; i < screen_num_desktops; ++i) {
- g_assert(!g_list_find(focus_order[i], c));
- if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
- focus_order[i] = g_list_insert(focus_order[i], c, 0);
- else
- focus_order[i] = g_list_insert(focus_order[i], c, 1);
- }
- } else {
- g_assert(!g_list_find(focus_order[d], c));
- if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
- focus_order[d] = g_list_insert(focus_order[d], c, 0);
- else
- focus_order[d] = g_list_insert(focus_order[d], c, 1);
- }
+ 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);
}
}
void focus_order_remove(ObClient *c)
{
- guint d, i;
-
- d = c->desktop;
- if (d == DESKTOP_ALL) {
- for (i = 0; i < screen_num_desktops; ++i)
- focus_order[i] = g_list_remove(focus_order[i], c);
- } else
- focus_order[d] = g_list_remove(focus_order[d], c);
+ focus_order = g_list_remove(focus_order, c);
}
-static void to_top(ObClient *c, guint d)
+void focus_order_to_top(ObClient *c)
{
- focus_order[d] = g_list_remove(focus_order[d], c);
+ focus_order = g_list_remove(focus_order, c);
if (!c->iconic) {
- focus_order[d] = g_list_prepend(focus_order[d], c);
+ focus_order = g_list_prepend(focus_order, c);
} else {
GList *it;
/* insert before first iconic window */
- for (it = focus_order[d];
+ for (it = focus_order;
it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
- focus_order[d] = g_list_insert_before(focus_order[d], it, c);
+ focus_order = g_list_insert_before(focus_order, it, c);
}
}
-void focus_order_to_top(ObClient *c)
-{
- guint d, i;
-
- d = c->desktop;
- if (d == DESKTOP_ALL) {
- for (i = 0; i < screen_num_desktops; ++i)
- to_top(c, i);
- } else
- to_top(c, d);
-}
-
-static void to_bottom(ObClient *c, guint d)
+void focus_order_to_bottom(ObClient *c)
{
- focus_order[d] = g_list_remove(focus_order[d], c);
+ focus_order = g_list_remove(focus_order, c);
if (c->iconic) {
- focus_order[d] = g_list_append(focus_order[d], c);
+ focus_order = g_list_append(focus_order, c);
} else {
GList *it;
/* insert before first iconic window */
- for (it = focus_order[d];
+ for (it = focus_order;
it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
- focus_order[d] = g_list_insert_before(focus_order[d], it, c);
+ focus_order = g_list_insert_before(focus_order, it, c);
}
}
-void focus_order_to_bottom(ObClient *c)
+ObClient *focus_order_find_first(guint desktop)
{
- guint d, i;
-
- d = c->desktop;
- if (d == DESKTOP_ALL) {
- for (i = 0; i < screen_num_desktops; ++i)
- to_bottom(c, i);
- } else
- to_bottom(c, d);
+ GList *it;
+ for (it = focus_order; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
+ return c;
+ }
+ return NULL;
}