]> 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 69a0e04948435fe002044c4679864745806cbe41..755b6ceabe787f8f3f94f86a042d4d25b361f4da 100644 (file)
@@ -37,6 +37,8 @@
 #include <glib.h>
 #include <assert.h>
 
+#define FOCUS_INDICATOR_WIDTH 5
+
 ObClient *focus_client = NULL;
 GList *focus_order = NULL;
 ObClient *focus_cycle_target = NULL;
@@ -59,7 +61,7 @@ static void focus_cycle_destructor(ObClient *client, gpointer data)
        be used
     */
     if (focus_cycle_target == client)
-        focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+        focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
 }
 
 static Window createWindow(Window parent, gulong mask,
@@ -168,7 +170,7 @@ void focus_set_client(ObClient *client)
        be used.
     */
     if (focus_cycle_target)
-        focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+        focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
 
     focus_client = client;
 
@@ -232,24 +234,19 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *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
+               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) && !c->iconic)
+            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->desktop == screen_desktop ||
-                            c->desktop == DESKTOP_ALL) &&
-                           c->type == OB_CLIENT_TYPE_DESKTOP && 
+                } else if (c->type == OB_CLIENT_TYPE_DESKTOP && 
                            desktop == NULL)
                     desktop = c;
             }
@@ -304,13 +301,8 @@ static void popup_cycle(ObClient *c, gboolean show)
         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)
@@ -334,8 +326,6 @@ static void popup_cycle(ObClient *c, gboolean 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);
@@ -352,10 +342,7 @@ void focus_cycle_draw_indicator()
         gint x, y, w, h;
         gint wt, wl, wr, wb;
 
-        wt = wl = wr = wb = MAX(3,
-                                MAX(1, MAX(ob_rr_theme->paddingx,
-                                           ob_rr_theme->paddingy)) * 2 +
-                                ob_rr_theme->fbwidth * 2);
+        wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
 
         x = focus_cycle_target->frame->area.x;
         y = focus_cycle_target->frame->area.y;
@@ -470,23 +457,39 @@ void focus_cycle_draw_indicator()
     }
 }
 
-static gboolean valid_focus_target(ObClient *ft)
+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 */
-    if ((ft->type == OB_CLIENT_TYPE_NORMAL ||
-         ft->type == OB_CLIENT_TYPE_DIALOG ||
-         (!client_has_group_siblings(ft) &&
-          (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
-           ft->type == OB_CLIENT_TYPE_MENU ||
-           ft->type == OB_CLIENT_TYPE_UTILITY))) &&
-        ((ft->can_focus || ft->focus_notify) &&
-         !ft->skip_pager &&
-         (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) &&
-        ft == client_focus_target(ft))
-        return TRUE;
+    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;
@@ -494,17 +497,17 @@ static gboolean valid_focus_target(ObClient *ft)
         for (it = ft->transients; it; it = g_slist_next(it)) {
             ObClient *c = it->data;
 
-            if (c->frame->visible)
+            if (frame_visible(c->frame))
                 return FALSE;
         }
         return TRUE;
     }
 */
-
-    return FALSE;
 }
 
-void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
+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;
@@ -548,7 +551,7 @@ void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
             if (it == NULL) it = g_list_last(list);
         }
         ft = it->data;
-        if (valid_focus_target(ft)) {
+        if (valid_focus_target(ft, all_desktops, dock_windows)) {
             if (interactive) {
                 if (ft != focus_cycle_target) { /* prevents flicker */
                     focus_cycle_target = ft;
@@ -582,7 +585,112 @@ done_cycle:
     return;
 }
 
-void focus_directional_cycle(ObDirection dir, gboolean interactive,
+/* 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;
@@ -604,12 +712,12 @@ void focus_directional_cycle(ObDirection dir, gboolean interactive,
     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; it; it = g_list_next(it))
-            if (valid_focus_target(it->data))
+            if (valid_focus_target(it->data, FALSE, dock_windows))
                 ft = it->data;
     }
         
This page took 0.028933 seconds and 4 git commands to generate.