]> Dogcows Code - chaz/openbox/blobdiff - openbox/focus.c
cycling between dock windows now possible with the <panels>yes</panels> option
[chaz/openbox] / openbox / focus.c
index 6652c5deb069b750cb819a96eb0d0eac298f5d70..4c663c3f124eabd30a223489263f1f24b6eb2120 100644 (file)
@@ -37,9 +37,9 @@
 #include <glib.h>
 #include <assert.h>
 
-ObClient *focus_client, *focus_hilite;
-GList *focus_order;
-ObClient *focus_cycle_target;
+ObClient *focus_client = NULL;
+GList *focus_order = NULL;
+ObClient *focus_cycle_target = NULL;
 
 struct {
     InternalWindow top;
@@ -59,7 +59,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);
 }
 
 static Window createWindow(Window parent, gulong mask,
@@ -81,7 +81,7 @@ void focus_startup(gboolean reconfig)
         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;
@@ -156,48 +156,34 @@ static void push_to_top(ObClient *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);
+        focus_cycle(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) {
         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);
     }
 }
 
@@ -207,14 +193,13 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
     ObClient *target = NULL;
     ObClient *desktop = NULL;
 
-    ob_debug("trying pointer stuff\n");
+    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) &&
-                    client_validate(target)) {
-                    ob_debug("found in pointer stuff\n");
+                if (client_normal(target) && client_can_focus(target)) {
+                    ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
                     return target;
                 }
     }
@@ -233,12 +218,15 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
         }
 #endif
 
-    ob_debug("trying omnipresentness\n");
-    if (allow_refocus && old && old->desktop == DESKTOP_ALL)
+    ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
+    if (allow_refocus && old && old->desktop == DESKTOP_ALL &&
+        client_normal(old))
+    {
         return old;
+    }
 
 
-    ob_debug("trying  the focus order\n");
+    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;
@@ -254,13 +242,15 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
                a splashscreen or a desktop window (save the desktop as a
                backup fallback though)
             */
-            if (client_can_focus(c) && client_validate(c) &&
-                c->desktop == screen_desktop && !c->iconic)
+            if (client_can_focus(c) && !c->iconic)
             {
-                if (client_normal(c)) {
-                    ob_debug("found in focus order\n");
+                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)
+                } else if ((c->desktop == screen_desktop ||
+                            c->desktop == DESKTOP_ALL) &&
+                           c->type == OB_CLIENT_TYPE_DESKTOP && 
+                           desktop == NULL)
                     desktop = c;
             }
         }
@@ -268,27 +258,40 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
     /* 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(gboolean allow_refocus)
 {
     ObClient *new;
-    ObClient *old;
-
-    /* save this before moving focus away to nothing */
-    old = focus_client;
+    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(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) {
@@ -335,6 +338,9 @@ void focus_cycle_draw_indicator()
         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)
@@ -462,23 +468,27 @@ void focus_cycle_draw_indicator()
     }
 }
 
-static gboolean valid_focus_target(ObClient *ft)
+static gboolean valid_focus_target(ObClient *ft, 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 ||
+              (!client_has_group_siblings(ft) &&
+               (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
+                ft->type == OB_CLIENT_TYPE_MENU ||
+                ft->type == OB_CLIENT_TYPE_UTILITY)));
+    ok = ok && (ft->can_focus || ft->focus_notify);
+    ok = ok && !ft->skip_pager;
+    ok = ok && (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL);
+    ok = ok && ft == client_focus_target(ft);
+    return ok;
 /*
     {
         GSList *it;
@@ -492,11 +502,10 @@ static gboolean valid_focus_target(ObClient *ft)
         return TRUE;
     }
 */
-
-    return FALSE;
 }
 
-void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
+void focus_cycle(gboolean forward, gboolean dock_windows,
+                 gboolean linear, gboolean interactive,
                  gboolean dialog, gboolean done, gboolean cancel)
 {
     static ObClient *first = NULL;
@@ -540,7 +549,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, dock_windows)) {
             if (interactive) {
                 if (ft != focus_cycle_target) { /* prevents flicker */
                     focus_cycle_target = ft;
@@ -574,7 +583,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;
@@ -596,12 +710,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, dock_windows))
                 ft = it->data;
     }
         
This page took 0.02785 seconds and 4 git commands to generate.