]> Dogcows Code - chaz/openbox/blobdiff - openbox/focus.c
add an allDesktops option to Next/PreviousWindow to alt-tab across all desktops.
[chaz/openbox] / openbox / focus.c
index 22036917a85a2719fc9de5701db976b8c7599af5..755b6ceabe787f8f3f94f86a042d4d25b361f4da 100644 (file)
@@ -1,7 +1,8 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
    focus.c for the Openbox window manager
-   Copyright (c) 2003        Ben Jansens
+   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
 #include "focus.h"
 #include "stacking.h"
 #include "popup.h"
+#include "render/render.h"
 
 #include <X11/Xlib.h>
 #include <glib.h>
 #include <assert.h>
 
-ObClient *focus_client;
-GList **focus_order; /* these lists are created when screen_startup
-                        sets the number of desktops */
-ObClient *focus_cycle_target;
+#define FOCUS_INDICATOR_WIDTH 5
+
+ObClient *focus_client = NULL;
+GList *focus_order = NULL;
+ObClient *focus_cycle_target = NULL;
+
+struct {
+    InternalWindow top;
+    InternalWindow left;
+    InternalWindow right;
+    InternalWindow bottom;
+} focus_indicator;
+
+RrAppearance *a_focus_indicator;
+RrColor *color_white;
 
 static ObIconPopup *focus_cycle_popup;
 
+static void focus_cycle_destructor(ObClient *client, gpointer data)
+{
+    /* end cycling if the target disappears. CurrentTime is fine, time won't
+       be used
+    */
+    if (focus_cycle_target == client)
+        focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+}
+
+static Window createWindow(Window parent, gulong mask,
+                           XSetWindowAttributes *attrib)
+{
+    return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
+                         RrDepth(ob_rr_inst), InputOutput,
+                         RrVisual(ob_rr_inst), mask, attrib);
+                       
+}
+
 void focus_startup(gboolean reconfig)
 {
     focus_cycle_popup = icon_popup_new(TRUE);
 
-    if (!reconfig)
+    if (!reconfig) {
+        XSetWindowAttributes attr;
+
+        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;
+        focus_indicator.right.obwin.type = Window_Internal;
+        focus_indicator.bottom.obwin.type = Window_Internal;
+
+        attr.override_redirect = True;
+        attr.background_pixel = BlackPixel(ob_display, ob_screen);
+        focus_indicator.top.win =
+            createWindow(RootWindow(ob_display, ob_screen),
+                         CWOverrideRedirect | CWBackPixel, &attr);
+        focus_indicator.left.win =
+            createWindow(RootWindow(ob_display, ob_screen),
+                         CWOverrideRedirect | CWBackPixel, &attr);
+        focus_indicator.right.win =
+            createWindow(RootWindow(ob_display, ob_screen),
+                         CWOverrideRedirect | CWBackPixel, &attr);
+        focus_indicator.bottom.win =
+            createWindow(RootWindow(ob_display, ob_screen),
+                         CWOverrideRedirect | CWBackPixel, &attr);
+
+        stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
+        stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
+        stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
+        stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
+
+        color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
+
+        a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
+        a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
+        a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
+        a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
+                                                        0, 0, 0);
+        a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
+        a_focus_indicator->texture[0].data.lineart.color = color_white;
+        a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
+        a_focus_indicator->texture[1].data.lineart.color = color_white;
+        a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
+        a_focus_indicator->texture[2].data.lineart.color = color_white;
+        a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
+        a_focus_indicator->texture[3].data.lineart.color = color_white;
+    }
 }
 
 void focus_shutdown(gboolean reconfig)
 {
-    guint i;
-
     icon_popup_free(focus_cycle_popup);
 
     if (!reconfig) {
-        for (i = 0; i < screen_num_desktops; ++i)
-            g_list_free(focus_order[i]);
-        g_free(focus_order);
+        client_remove_destructor(focus_cycle_destructor);
 
         /* reset focus to root */
-        XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
-                       event_lasttime);
+        XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
+
+        RrColorFree(color_white);
+
+        RrAppearanceFree(a_focus_indicator);
+
+        XDestroyWindow(ob_display, focus_indicator.top.win);
+        XDestroyWindow(ob_display, focus_indicator.left.win);
+        XDestroyWindow(ob_display, focus_indicator.right.win);
+        XDestroyWindow(ob_display, focus_indicator.bottom.win);
     }
 }
 
 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) {
-       /* when nothing will be focused, send focus to the backup target */
-       XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
-                       event_lasttime);
-        XSync(ob_display, FALSE);
-    }
-
-    /* in the middle of cycling..? kill it. */
+    /* 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);
+        focus_cycle(TRUE, TRUE, 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) {
@@ -117,109 +189,104 @@ void focus_set_client(ObClient *client)
     }
 }
 
-static gboolean focus_under_pointer()
+ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
 {
-    ObClient *c;
+    GList *it;
+    ObClient *target = NULL;
+    ObClient *desktop = NULL;
+
+    ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
+    if (config_focus_follow && !config_focus_last)
+    {
+        if ((target = client_under_pointer()))
+            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 ((c = client_under_pointer()))
-        return client_normal(c) && client_focus(c);
-    return FALSE;
-}
+#if 0
+        /* try for group relations */
+        if (old->group) {
+            GSList *sit;
 
-/* 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 = it->next) {
-        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;
-}
+            for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
+                for (sit = old->group->members; sit; sit = g_slist_next(sit))
+                    if (sit->data == it->data)
+                        if (sit->data != old && client_normal(sit->data))
+                            if (client_can_focus(sit->data))
+                                return sit->data;
+        }
+#endif
 
-static gboolean 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 FALSE;
-        target = top; /* no transient, keep the top */
+    ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
+    if (allow_refocus && old && old->desktop == DESKTOP_ALL &&
+        client_normal(old))
+    {
+        return old;
     }
-    return client_focus(target);
+
+
+    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. this includes if it is visible right now
+               2. it is on the current desktop. this ignores omnipresent
+               windows, which are problematic in their own rite.
+               3. 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))
+            {
+                if (c->desktop == screen_desktop && 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 == NULL)
+                    desktop = c;
+            }
+        }
+
+    /* 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.)
+    */
+    ob_debug_type(OB_DEBUG_FOCUS, "found desktop: \n", !!desktop);
+    return desktop;   
 }
 
-void focus_fallback(ObFocusFallbackType type)
+void focus_fallback(gboolean allow_refocus)
 {
-    GList *it;
-    ObClient *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);
-
-    if (config_focus_follow && focus_under_pointer())
-        return;
-
-    if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
-        /* try for transient relations */
-        if (old->transient_for) {
-            if (old->transient_for == OB_TRAN_GROUP) {
-                for (it = focus_order[screen_desktop]; it; it = it->next) {
-                    GSList *sit;
-
-                    for (sit = old->group->members; sit; sit = sit->next)
-                        if (sit->data == it->data)
-                            if (focus_fallback_transient(sit->data, old))
-                                return;
-                }
-            } else {
-                if (focus_fallback_transient(old->transient_for, old))
-                    return;
-            }
-        }
+    focus_nothing();
 
-#if 0
-        /* try for group relations */
-        if (old->group) {
-            GSList *sit;
+    if ((new = focus_fallback_target(allow_refocus, old)))
+        client_focus(new);
+}
 
-            for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
-                for (sit = old->group->members; sit; sit = sit->next)
-                    if (sit->data == it->data)
-                        if (sit->data != old && client_normal(sit->data))
-                            if (client_can_focus(sit->data)) {
-                                gboolean r = client_focus(sit->data);
-                                assert(r);
-                                return;
-                            }
-        }
-#endif
+void focus_nothing()
+{
+    /* Install our own colormap */
+    if (focus_client != NULL) {
+        screen_install_colormap(focus_client, FALSE);
+        screen_install_colormap(NULL, TRUE);
     }
 
-    for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
-        if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
-            if (client_normal(it->data) &&
-                /* dont fall back to 'anonymous' fullscreen windows. theres no
-                   checks for this is in transient/group fallbacks, so they can
-                   be fallback targets there. */
-                !((ObClient*)it->data)->fullscreen &&
-                client_can_focus(it->data)) {
-                gboolean r = client_focus(it->data);
-                assert(r);
-                return;
-            }
+    focus_client = NULL;
 
-    /* nothing to focus, and already set it to none above */
+    /* 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)
@@ -229,31 +296,25 @@ static void popup_cycle(ObClient *c, gboolean show)
     } else {
         Rect *a;
         ObClient *p = c;
-        char *title;
+        gchar *title = NULL;
 
         a = screen_physical_area_monitor(0);
         icon_popup_position(focus_cycle_popup, CenterGravity,
                             a->x + a->width / 2, a->y + a->height / 2);
-/*        icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
-        icon_popup_show(focus_cycle_popup, c->title,
-                        client_icon(c, a->height/16, a->height/16));
-*/
-        /* XXX the size and the font extents need to be related on some level
-         */
-        icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
+        icon_popup_width(focus_cycle_popup, MAX(a->width/3, POPUP_WIDTH));
+        icon_popup_height(focus_cycle_popup, POPUP_HEIGHT);
 
         /* use the transient's parent's title/icon */
         while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
             p = p->transient_for;
 
-        if (p == c)
-            title = NULL;
-        else
-            title = g_strconcat((c->iconic ? c->icon_title : c->title),
+        if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
+            title = g_strdup(p->iconic ? p->icon_title : p->title);
+            /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
                                 " - ",
                                 (p->iconic ? p->icon_title : p->title),
                                 NULL);
-
+            */
         icon_popup_show(focus_cycle_popup,
                         (title ? title :
                          (c->iconic ? c->icon_title : c->title)),
@@ -262,46 +323,219 @@ static void popup_cycle(ObClient *c, gboolean show)
     }
 }
 
-static gboolean valid_focus_target(ObClient *ft)
+void focus_cycle_draw_indicator()
+{
+    if (!focus_cycle_target) {
+        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 */
+        event_ignore_queued_enters();
+    } else {
+        /*
+          if (focus_cycle_target)
+              frame_adjust_focus(focus_cycle_target->frame, FALSE);
+          frame_adjust_focus(focus_cycle_target->frame, TRUE);
+        */
+        gint x, y, w, h;
+        gint wt, wl, wr, wb;
+
+        wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
+
+        x = focus_cycle_target->frame->area.x;
+        y = focus_cycle_target->frame->area.y;
+        w = focus_cycle_target->frame->area.width;
+        h = wt;
+
+        XMoveResizeWindow(ob_display, focus_indicator.top.win,
+                          x, y, w, h);
+        a_focus_indicator->texture[0].data.lineart.x1 = 0;
+        a_focus_indicator->texture[0].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[0].data.lineart.x2 = 0;
+        a_focus_indicator->texture[0].data.lineart.y2 = 0;
+        a_focus_indicator->texture[1].data.lineart.x1 = 0;
+        a_focus_indicator->texture[1].data.lineart.y1 = 0;
+        a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[1].data.lineart.y2 = 0;
+        a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y1 = 0;
+        a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
+        a_focus_indicator->texture[3].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
+        a_focus_indicator->texture[3].data.lineart.y2 = h-1;
+        RrPaint(a_focus_indicator, focus_indicator.top.win,
+                w, h);
+
+        x = focus_cycle_target->frame->area.x;
+        y = focus_cycle_target->frame->area.y;
+        w = wl;
+        h = focus_cycle_target->frame->area.height;
+
+        XMoveResizeWindow(ob_display, focus_indicator.left.win,
+                          x, y, w, h);
+        a_focus_indicator->texture[0].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[0].data.lineart.y1 = 0;
+        a_focus_indicator->texture[0].data.lineart.x2 = 0;
+        a_focus_indicator->texture[0].data.lineart.y2 = 0;
+        a_focus_indicator->texture[1].data.lineart.x1 = 0;
+        a_focus_indicator->texture[1].data.lineart.y1 = 0;
+        a_focus_indicator->texture[1].data.lineart.x2 = 0;
+        a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x1 = 0;
+        a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[3].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
+        a_focus_indicator->texture[3].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
+        RrPaint(a_focus_indicator, focus_indicator.left.win,
+                w, h);
+
+        x = focus_cycle_target->frame->area.x +
+            focus_cycle_target->frame->area.width - wr;
+        y = focus_cycle_target->frame->area.y;
+        w = wr;
+        h = focus_cycle_target->frame->area.height ;
+
+        XMoveResizeWindow(ob_display, focus_indicator.right.win,
+                          x, y, w, h);
+        a_focus_indicator->texture[0].data.lineart.x1 = 0;
+        a_focus_indicator->texture[0].data.lineart.y1 = 0;
+        a_focus_indicator->texture[0].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[0].data.lineart.y2 = 0;
+        a_focus_indicator->texture[1].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[1].data.lineart.y1 = 0;
+        a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x2 = 0;
+        a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[3].data.lineart.x1 = 0;
+        a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
+        a_focus_indicator->texture[3].data.lineart.x2 = 0;
+        a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
+        RrPaint(a_focus_indicator, focus_indicator.right.win,
+                w, h);
+
+        x = focus_cycle_target->frame->area.x;
+        y = focus_cycle_target->frame->area.y +
+            focus_cycle_target->frame->area.height - wb;
+        w = focus_cycle_target->frame->area.width;
+        h = wb;
+
+        XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
+                          x, y, w, h);
+        a_focus_indicator->texture[0].data.lineart.x1 = 0;
+        a_focus_indicator->texture[0].data.lineart.y1 = 0;
+        a_focus_indicator->texture[0].data.lineart.x2 = 0;
+        a_focus_indicator->texture[0].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[1].data.lineart.x1 = 0;
+        a_focus_indicator->texture[1].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+        a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+        a_focus_indicator->texture[2].data.lineart.y2 = 0;
+        a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
+        a_focus_indicator->texture[3].data.lineart.y1 = 0;
+        a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
+        a_focus_indicator->texture[3].data.lineart.y2 = 0;
+        RrPaint(a_focus_indicator, focus_indicator.bottom.win,
+                w, h);
+
+        XMapWindow(ob_display, focus_indicator.top.win);
+        XMapWindow(ob_display, focus_indicator.left.win);
+        XMapWindow(ob_display, focus_indicator.right.win);
+        XMapWindow(ob_display, focus_indicator.bottom.win);
+    }
+}
+
+static gboolean valid_focus_target(ObClient *ft,
+                                   gboolean all_desktops,
+                                   gboolean dock_windows)
 {
+    gboolean ok = FALSE;
     /* we don't use client_can_focus here, because that doesn't let you
        focus an iconic window, but we want to be able to, so we just check
        if the focus flags on the window allow it, and its on the current
        desktop */
-    return (ft->transients == NULL && client_normal(ft) &&
-            ((ft->can_focus || ft->focus_notify) &&
-             !ft->skip_taskbar &&
-             (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
+    if (dock_windows)
+        ok = ft->type == OB_CLIENT_TYPE_DOCK;
+    else
+        ok = (ft->type == OB_CLIENT_TYPE_NORMAL ||
+              ft->type == OB_CLIENT_TYPE_DIALOG ||
+              ((ft->type == OB_CLIENT_TYPE_TOOLBAR ||
+                ft->type == OB_CLIENT_TYPE_MENU ||
+                ft->type == OB_CLIENT_TYPE_UTILITY) &&
+               /* let alt-tab go to these windows when a window in its group
+                  already has focus ... */
+               ((focus_client && ft->group == focus_client->group) ||
+                /* ... or if there are no application windows in its group */
+                !client_has_application_group_siblings(ft))));
+    ok = ok && (ft->can_focus || ft->focus_notify);
+    if (!dock_windows && /* use dock windows that skip taskbar too */
+        !(ft->type == OB_CLIENT_TYPE_TOOLBAR || /* also, if we actually are */
+          ft->type == OB_CLIENT_TYPE_MENU ||    /* being allowed to target */
+          ft->type == OB_CLIENT_TYPE_UTILITY))  /* one of these, don't let */
+        ok = ok && !ft->skip_taskbar;           /*  skip taskbar stop us */
+    if (!all_desktops)
+        ok = ok && (ft->desktop == screen_desktop ||
+                    ft->desktop == DESKTOP_ALL);
+    ok = ok && ft == client_focus_target(ft);
+    return ok;
+/*
+    {
+        GSList *it;
+
+        for (it = ft->transients; it; it = g_slist_next(it)) {
+            ObClient *c = it->data;
+
+            if (frame_visible(c->frame))
+                return FALSE;
+        }
+        return TRUE;
+    }
+*/
 }
 
-void focus_cycle(gboolean forward, gboolean linear,
+void focus_cycle(gboolean forward, gboolean all_desktops,
+                 gboolean dock_windows,
+                 gboolean linear, gboolean interactive,
                  gboolean dialog, gboolean done, gboolean cancel)
 {
     static ObClient *first = NULL;
     static ObClient *t = NULL;
     static GList *order = NULL;
     GList *it, *start, *list;
-    ObClient *ft;
+    ObClient *ft = NULL;
 
-    if (cancel) {
-        if (focus_cycle_target)
-            frame_adjust_focus(focus_cycle_target->frame, FALSE);
-        if (focus_client)
-            frame_adjust_focus(focus_client->frame, TRUE);
-        focus_cycle_target = NULL;
-        goto done_cycle;
-    } else if (done && dialog) {
-        goto done_cycle;
-    }
+    if (interactive) {
+        if (cancel) {
+            focus_cycle_target = NULL;
+            goto done_cycle;
+        } else if (done)
+            goto done_cycle;
 
-    if (!focus_order[screen_desktop])
-        goto done_cycle;
+        if (!focus_order)
+            goto done_cycle;
 
-    if (!first) first = focus_client;
-    if (!focus_cycle_target) focus_cycle_target = focus_client;
+        if (!first) first = focus_client;
 
-    if (linear) list = client_list;
-    else        list = focus_order[screen_desktop];
+        if (linear) list = client_list;
+        else        list = focus_order;
+    } else {
+        if (!focus_order)
+            goto done_cycle;
+        list = client_list;
+    }
+    if (!focus_cycle_target) focus_cycle_target = focus_client;
 
     start = it = g_list_find(list, focus_cycle_target);
     if (!start) /* switched desktops or something? */
@@ -316,23 +550,26 @@ void focus_cycle(gboolean forward, gboolean linear,
             it = it->prev;
             if (it == NULL) it = g_list_last(list);
         }
-        /*ft = client_focus_target(it->data);*/
         ft = it->data;
-        if (valid_focus_target(ft)) {
-            if (ft != focus_cycle_target) { /* prevents flicker */
-                if (focus_cycle_target)
-                    frame_adjust_focus(focus_cycle_target->frame, FALSE);
+        if (valid_focus_target(ft, all_desktops, dock_windows)) {
+            if (interactive) {
+                if (ft != focus_cycle_target) { /* prevents flicker */
+                    focus_cycle_target = ft;
+                    focus_cycle_draw_indicator();
+                }
+                popup_cycle(ft, dialog);
+                return;
+            } else if (ft != focus_cycle_target) {
                 focus_cycle_target = ft;
-                frame_adjust_focus(focus_cycle_target->frame, TRUE);
+                done = TRUE;
+                break;
             }
-            popup_cycle(ft, dialog);
-            return;
         }
     } while (it != start);
 
 done_cycle:
     if (done && focus_cycle_target)
-        client_activate(focus_cycle_target, FALSE);
+        client_activate(focus_cycle_target, FALSE, TRUE);
 
     t = NULL;
     first = NULL;
@@ -340,50 +577,154 @@ done_cycle:
     g_list_free(order);
     order = NULL;
 
-    popup_cycle(ft, FALSE);
+    if (interactive) {
+        focus_cycle_draw_indicator();
+        popup_cycle(ft, FALSE);
+    }
 
     return;
 }
 
-void focus_directional_cycle(ObDirection dir,
+/* this be mostly ripped from fvwm */
+ObClient *focus_find_directional(ObClient *c, ObDirection dir,
+                                 gboolean dock_windows) 
+{
+    gint my_cx, my_cy, his_cx, his_cy;
+    gint offset = 0;
+    gint distance = 0;
+    gint score, best_score;
+    ObClient *best_client, *cur;
+    GList *it;
+
+    if(!client_list)
+        return NULL;
+
+    /* first, find the centre coords of the currently focused window */
+    my_cx = c->frame->area.x + c->frame->area.width / 2;
+    my_cy = c->frame->area.y + c->frame->area.height / 2;
+
+    best_score = -1;
+    best_client = NULL;
+
+    for(it = g_list_first(client_list); it; it = g_list_next(it)) {
+        cur = it->data;
+
+        /* the currently selected window isn't interesting */
+        if(cur == c)
+            continue;
+        if (!dock_windows && !client_normal(cur))
+            continue;
+        if (dock_windows && cur->type != OB_CLIENT_TYPE_DOCK)
+            continue;
+        /* using c->desktop instead of screen_desktop doesn't work if the
+         * current window was omnipresent, hope this doesn't have any other
+         * side effects */
+        if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+            continue;
+        if(cur->iconic)
+            continue;
+        if(!(client_focus_target(cur) == cur &&
+             client_can_focus(cur)))
+            continue;
+
+        /* find the centre coords of this window, from the
+         * currently focused window's point of view */
+        his_cx = (cur->frame->area.x - my_cx)
+            + cur->frame->area.width / 2;
+        his_cy = (cur->frame->area.y - my_cy)
+            + cur->frame->area.height / 2;
+
+        if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
+           dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
+            gint tx;
+            /* Rotate the diagonals 45 degrees counterclockwise.
+             * To do this, multiply the matrix /+h +h\ with the
+             * vector (x y).                   \-h +h/
+             * h = sqrt(0.5). We can set h := 1 since absolute
+             * distance doesn't matter here. */
+            tx = his_cx + his_cy;
+            his_cy = -his_cx + his_cy;
+            his_cx = tx;
+        }
+
+        switch(dir) {
+        case OB_DIRECTION_NORTH:
+        case OB_DIRECTION_SOUTH:
+        case OB_DIRECTION_NORTHEAST:
+        case OB_DIRECTION_SOUTHWEST:
+            offset = (his_cx < 0) ? -his_cx : his_cx;
+            distance = ((dir == OB_DIRECTION_NORTH ||
+                         dir == OB_DIRECTION_NORTHEAST) ?
+                        -his_cy : his_cy);
+            break;
+        case OB_DIRECTION_EAST:
+        case OB_DIRECTION_WEST:
+        case OB_DIRECTION_SOUTHEAST:
+        case OB_DIRECTION_NORTHWEST:
+            offset = (his_cy < 0) ? -his_cy : his_cy;
+            distance = ((dir == OB_DIRECTION_WEST ||
+                         dir == OB_DIRECTION_NORTHWEST) ?
+                        -his_cx : his_cx);
+            break;
+        }
+
+        /* the target must be in the requested direction */
+        if(distance <= 0)
+            continue;
+
+        /* Calculate score for this window.  The smaller the better. */
+        score = distance + offset;
+
+        /* windows more than 45 degrees off the direction are
+         * heavily penalized and will only be chosen if nothing
+         * else within a million pixels */
+        if(offset > distance)
+            score += 1000000;
+
+        if(best_score == -1 || score < best_score)
+            best_client = cur,
+                best_score = score;
+    }
+
+    return best_client;
+}
+
+void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
+                             gboolean interactive,
                              gboolean dialog, gboolean done, gboolean cancel)
 {
     static ObClient *first = NULL;
     ObClient *ft = NULL;
 
+    if (!interactive)
+        return;
+
     if (cancel) {
-        if (focus_cycle_target)
-            frame_adjust_focus(focus_cycle_target->frame, FALSE);
-        if (focus_client)
-            frame_adjust_focus(focus_client->frame, TRUE);
         focus_cycle_target = NULL;
         goto done_cycle;
-    } else if (done && dialog) {
+    } else if (done)
         goto done_cycle;
-    }
 
-    if (!focus_order[screen_desktop])
+    if (!focus_order)
         goto done_cycle;
 
     if (!first) first = focus_client;
     if (!focus_cycle_target) focus_cycle_target = focus_client;
 
     if (focus_cycle_target)
-        ft = client_find_directional(focus_cycle_target, dir);
+        ft = focus_find_directional(focus_cycle_target, dir, dock_windows);
     else {
         GList *it;
 
-        for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
-            if (valid_focus_target(it->data))
+        for (it = focus_order; it; it = g_list_next(it))
+            if (valid_focus_target(it->data, FALSE, dock_windows))
                 ft = it->data;
     }
         
     if (ft) {
         if (ft != focus_cycle_target) {/* prevents flicker */
-            if (focus_cycle_target)
-                frame_adjust_focus(focus_cycle_target->frame, FALSE);
             focus_cycle_target = ft;
-            frame_adjust_focus(focus_cycle_target->frame, TRUE);
+            focus_cycle_draw_indicator();
         }
     }
     if (focus_cycle_target) {
@@ -395,11 +736,12 @@ void focus_directional_cycle(ObDirection dir,
 
 done_cycle:
     if (done && focus_cycle_target)
-        client_activate(focus_cycle_target, FALSE);
+        client_activate(focus_cycle_target, FALSE, TRUE);
 
     first = NULL;
     focus_cycle_target = NULL;
 
+    focus_cycle_draw_indicator();
     popup_cycle(ft, FALSE);
 
     return;
@@ -407,89 +749,61 @@ done_cycle:
 
 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) {
-                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
-             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];
-             it && !((ObClient*)it->data)->iconic; it = it->next);
-        focus_order[d] = g_list_insert_before(focus_order[d], it, c);
+        for (it = focus_order;
+             it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+        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];
-             it && !((ObClient*)it->data)->iconic; it = it->next);
-        g_list_insert_before(focus_order[d], it, c);
+        for (it = focus_order;
+             it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+        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;
 }
This page took 0.041033 seconds and 4 git commands to generate.