]> Dogcows Code - chaz/openbox/blobdiff - openbox/focus.c
when you focus a window, bring any modal children it has to that desktop
[chaz/openbox] / openbox / focus.c
index 04ef7cd4b37ca63665c77d8d856e88346d1ca567..87f589f2d055cc24eeb2dc400662e24e20beb7c5 100644 (file)
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   focus.c for the Openbox window manager
+   Copyright (c) 2006        Mikael Magnusson
+   Copyright (c) 2003-2007   Dana Jansens
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
 #include "event.h"
 #include "openbox.h"
+#include "grab.h"
 #include "client.h"
 #include "config.h"
-#include "frame.h"
+#include "focus_cycle.h"
 #include "screen.h"
-#include "group.h"
 #include "prop.h"
-#include "dispatch.h"
+#include "keyboard.h"
 #include "focus.h"
-#include "parse.h"
 #include "stacking.h"
 
 #include <X11/Xlib.h>
 #include <glib.h>
 
-Client *focus_client = NULL;
-GList **focus_order = NULL; /* these lists are created when screen_startup
-                               sets the number of desktops */
-
-Window focus_backup = None;
+#define FOCUS_INDICATOR_WIDTH 6
 
-static gboolean noreorder = 0;
+ObClient *focus_client = NULL;
+GList *focus_order = NULL;
 
-void focus_startup()
+void focus_startup(gboolean reconfig)
 {
-    /* create the window which gets focus when no clients get it. Have to
-       make it override-redirect so we don't try manage it, since it is
-       mapped. */
-    XSetWindowAttributes attrib;
-
-    focus_client = NULL;
-
-    attrib.override_redirect = TRUE;
-    focus_backup = XCreateWindow(ob_display, ob_root,
-                                -100, -100, 1, 1, 0,
-                                 CopyFromParent, InputOutput, CopyFromParent,
-                                 CWOverrideRedirect, &attrib);
-    XMapWindow(ob_display, focus_backup);
-    stacking_raise_internal(focus_backup);
+    if (reconfig) return;
 
     /* start with nothing focused */
-    focus_set_client(NULL);
+    focus_nothing();
 }
 
-void focus_shutdown()
+void focus_shutdown(gboolean reconfig)
 {
-    guint i;
-
-    for (i = 0; i < screen_num_desktops; ++i)
-        g_list_free(focus_order[i]);
-    g_free(focus_order);
-    focus_order = NULL;
-
-    XDestroyWindow(ob_display, focus_backup);
+    if (reconfig) return;
 
     /* reset focus to root */
-    XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
-                   event_lasttime);
+    XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
 }
 
-static void push_to_top(Client *client)
+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(Client *client)
+void focus_set_client(ObClient *client)
 {
     Window active;
-    Client *old;
+
+    ob_debug_type(OB_DEBUG_FOCUS,
+                  "focus_set_client 0x%lx\n", client ? client->window : 0);
+
+    if (focus_client == client)
+        return;
 
     /* uninstall the old colormap, and install the new one */
     screen_install_colormap(focus_client, FALSE);
     screen_install_colormap(client, TRUE);
 
-    if (client == NULL) {
-       /* when nothing will be focused, send focus to the backup target */
-       XSetInputFocus(ob_display, focus_backup, RevertToPointerRoot,
-                       event_lasttime);
-        XSync(ob_display, FALSE);
-    }
+    /* in the middle of cycling..? kill it. */
+    focus_cycle_stop(focus_client);
+    focus_cycle_stop(client);
 
-    old = focus_client;
     focus_client = client;
 
-    /* move to the top of the list */
-    if (noreorder)
-        --noreorder;
-    else 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 */
-    active = client ? client->window : None;
-    PROP_SET32(ob_root, net_active_window, window, active);
-
-    if (focus_client != NULL)
-        dispatch_client(Event_Client_Focus, focus_client, 0, 0);
-    if (old != NULL)
-        dispatch_client(Event_Client_Unfocus, old, 0, 0);
+    /* 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);
+    }
 }
 
-static gboolean focus_under_pointer()
+static ObClient* focus_fallback_target(gboolean allow_refocus,
+                                       gboolean allow_pointer,
+                                       ObClient *old)
 {
-    Window w;
-    int i, x, y;
-    guint u;
     GList *it;
+    ObClient *c;
+
+    ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
+    if (allow_pointer && config_focus_follow)
+        if ((c = client_under_pointer()) &&
+            (allow_refocus || c != old) &&
+            (client_normal(c) &&
+             client_focus(c)))
+        {
+            ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
+            return c;
+        }
+
+    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)
+        */
+        if (c->desktop == screen_desktop &&
+            client_normal(c) &&
+            (allow_refocus || c != old) &&
+            client_focus(c))
+        {
+            ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
+            return c;
+        }
+    }
 
-    if (XQueryPointer(ob_display, ob_root, &w, &w, &x, &y, &i, &i, &u)) {
-        for (it = stacking_list; it != NULL; it = it->next) {
-            Client *c = it->data;
-            if (c->desktop == screen_desktop &&
-                RECT_CONTAINS(c->frame->area, x, y))
-                break;
+    ob_debug_type(OB_DEBUG_FOCUS, "trying a desktop window\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)
+        */
+        if (c->type == OB_CLIENT_TYPE_DESKTOP &&
+            (allow_refocus || c != old) &&
+            client_focus(c))
+        {
+            ob_debug_type(OB_DEBUG_FOCUS, "found a desktop window\n");
+            return c;
         }
-        if (it != NULL)
-            return client_normal(it->data) && client_focus(it->data);
     }
-    return FALSE;
+
+    return NULL;
 }
 
-void focus_fallback(FallbackType type)
+ObClient* focus_fallback(gboolean allow_refocus, gboolean allow_pointer)
 {
-    GList *it;
-    Client *old = NULL;
-
-    old = focus_client;
+    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);
+       and such, and then when we try focus them, we won't get a FocusIn
+       event at all for them. */
+    focus_nothing();
 
-    if (!(type == Fallback_Desktop ?
-          config_focus_last_on_desktop : config_focus_last)) {
-        if (config_focus_follow) focus_under_pointer();
-        return;
+    new = focus_fallback_target(allow_refocus, allow_pointer, old);
+    /* get what was really focused */
+    if (new) new = client_focus_target(new);
+
+    return new;
+}
+
+void focus_nothing()
+{
+    /* Install our own colormap */
+    if (focus_client != NULL) {
+        screen_install_colormap(focus_client, FALSE);
+        screen_install_colormap(NULL, TRUE);
     }
 
-    if (type == Fallback_Unfocusing && old && old->transient_for) {
-        if (old->transient_for == TRAN_GROUP) {
-            for (it = focus_order[screen_desktop]; it != NULL; it = it->next) {
-                GSList *sit;
-
-                for (sit = old->group->members; sit; sit = sit->next)
-                    if (sit->data == it->data && client_focus(sit->data))
-                        return;
-            }
-        } else {
-            if (client_normal(old->transient_for))
-                if (client_focus(old->transient_for))
-                    return;
-        }
+    /* 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
+       and ignore them !
+
+       actions should not rely on being able to move focus during an
+       interactive grab.
+    */
+    if (keyboard_interactively_grabbed())
+        keyboard_interactive_cancel();
+
+    /* 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);
     }
 
-    for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
-        if (type != Fallback_Unfocusing || it->data != old)
-            if (client_normal(it->data) &&
-                ((Client*)it->data)->frame->visible &&
-                client_focus(it->data))
-                return;
+    /* in the middle of cycling..? kill it. */
+    focus_cycle_stop(c);
+}
 
-    /* nothing to focus */
-    focus_set_client(NULL);
+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);
 }
 
-Client *focus_cycle(gboolean forward, gboolean linear, gboolean done,
-                 gboolean cancel)
+void focus_order_to_top(ObClient *c)
 {
-    static Client *first = NULL;
-    static Client *t = NULL;
-    static GList *order = NULL;
-    GList *it, *start, *list;
-    Client *ft;
-
-    if (cancel) {
-        if (first) client_focus(first);
-        goto done_cycle;
-    } else if (done) {
-        if (focus_client) {
-            push_to_top(focus_client); /* move to top of focus_order */
-            stacking_raise(focus_client);
-        }
-        goto done_cycle;
+    focus_order = g_list_remove(focus_order, c);
+    if (!c->iconic) {
+        focus_order = g_list_prepend(focus_order, c);
+    } else {
+        GList *it;
+
+        /* insert before first iconic window */
+        for (it = focus_order;
+             it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+        focus_order = g_list_insert_before(focus_order, it, c);
     }
-    if (!first) first = focus_client;
-
-    if (linear) list = client_list;
-    else        list = focus_order[screen_desktop];
-
-    start = it = g_list_find(list, focus_client);
-    if (!start) /* switched desktops or something? */
-        start = it = forward ? g_list_last(list) : g_list_first(list);
-    if (!start) goto done_cycle;
-
-    do {
-        if (forward) {
-            it = it->next;
-            if (it == NULL) it = list;
-        } else {
-            it = it->prev;
-            if (it == NULL) it = g_list_last(list);
-        }
-        ft = client_focus_target(it->data);
-        if (ft == it->data && focus_client != ft && client_normal(ft) &&
-            ft->frame->visible && client_focus(ft)) {
-            noreorder++; /* avoid reordering the focus_order */
-            return ft;
-        }
-    } while (it != start);
-    return NULL;
+}
 
-done_cycle:
-    t = NULL;
-    first = NULL;
-    g_list_free(order);
-    order = NULL;
+void focus_order_to_bottom(ObClient *c)
+{
+    focus_order = g_list_remove(focus_order, c);
+    if (c->iconic) {
+        focus_order = g_list_append(focus_order, c);
+    } else {
+        GList *it;
+
+        /* insert before first iconic window */
+        for (it = focus_order;
+             it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+        focus_order = g_list_insert_before(focus_order, it, c);
+    }
+}
+
+ObClient *focus_order_find_first(guint desktop)
+{
+    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;
 }
This page took 0.032223 seconds and 4 git commands to generate.