]> git.lizzy.rs Git - bspwm.git/commitdiff
New setting: `focus_by_distance`
authorBastien Dejean <nihilhill@gmail.com>
Tue, 7 May 2013 20:45:14 +0000 (22:45 +0200)
committerBastien Dejean <nihilhill@gmail.com>
Tue, 7 May 2013 20:45:14 +0000 (22:45 +0200)
The aforementioned setting, when set, makes the focus movements based on
the distances between the window sides.

The `{prev,next}_leaf` functions were taught not to climb above their
roofs.

13 files changed:
README.md
bash_completion
bspwm.1
ewmh.c
helpers.c
helpers.h
messages.c
settings.c
settings.h
tree.c
tree.h
window.c
window.h

index 9af84c6379072421fa3a1598b70b24963f07d2cd..7d3e113ae365a082e19915c25b4c39fe76ce3ff1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -225,6 +225,8 @@ Colors are either [X color names](http://en.wikipedia.org/wiki/X11_color_names)
 
 - `auto_alternate` — Whether to interpret two consecutive identical `use` messages as an `alternate` message.
 
+- `focus_by_distance` — Whether to use window or leaf distance for focus movement.
+
 ## Environment Variables
 
 - `BSPWM_SOCKET` — The path of the socket used for the communication between `bspc` and `bspwm`.
index 588c73668d709ee75d3421d975a7fcd8a3d33be3..9121915b9a56cfb00c775980572a0841f903ae0d 100644 (file)
@@ -2,7 +2,7 @@ _bspc()
 {
     local messages='get set list list_desktops list_monitors list_windows list_rules list_history presel cancel ratio pad focus shift swap push pull cycle nearest circulate grab_pointer track_pointer ungrab_pointer toggle_fullscreen toggle_floating toggle_locked toggle_visibility close kill send_to drop_to send_to_monitor drop_to_monitor use use_monitor alternate alternate_desktop alternate_monitor add add_in rename_monitor rename cycle_monitor cycle_desktop layout cycle_layout rotate flip balance rule remove_rule put_status adopt_orphans restore quit'
 
-    local settings='focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color urgent_border_color border_width window_gap split_ratio top_padding right_padding bottom_padding left_padding wm_name borderless_monocle gapless_monocle focus_follows_pointer adaptative_raise apply_shadow_property auto_alternate'
+    local settings='focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color urgent_border_color border_width window_gap split_ratio top_padding right_padding bottom_padding left_padding wm_name borderless_monocle gapless_monocle focus_follows_pointer adaptative_raise apply_shadow_property auto_alternate focus_by_distance'
 
     COMPREPLY=()
 
diff --git a/bspwm.1 b/bspwm.1
index 628b1b0443b4995edbdd26c03d2acfaf478a2ca0..4ce17c669505617b405a9bbc5a80efbdefda4a77 100644 (file)
--- a/bspwm.1
+++ b/bspwm.1
@@ -338,6 +338,9 @@ Whether to interpret two consecutive identical
 messages as an
 .B alternate
 message.
+.TP
+.I focus_by_distance
+Whether to use window or leaf distance for focus movement.
 .SH ENVIRONMENT VARIABLES
 .TP
 .I BSPWM_SOCKET
diff --git a/ewmh.c b/ewmh.c
index eb47ac5ab66374d0666723a2b85b589109a10dc4..cf7b2060293562393ad1b92545137f9e5d4107ba 100644 (file)
--- a/ewmh.c
+++ b/ewmh.c
@@ -110,7 +110,7 @@ void ewmh_update_client_list(void)
 
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n))
+            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
                 wins[i++] = n->client->window;
 
     if (i != num_clients)
index 28bed8e500a4382fd96654daaf5115097651808b..45e3e6a11c8c4cea4ae22d2187720d9c243d3882 100644 (file)
--- a/helpers.c
+++ b/helpers.c
@@ -1,5 +1,6 @@
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 #include <xcb/xcb.h>
 #include <xcb/xcb_event.h>
 #include "bspwm.h"
@@ -51,3 +52,8 @@ uint32_t get_color(char *col)
 
     return pxl;
 }
+
+double distance(xcb_point_t a, xcb_point_t b)
+{
+    return hypot(a.x - b.x, a.y - b.y);
+}
index cc5a3d363041748a59d3276ce4ae8352a1b4d31e..764fd09178af61693ba6e3deb9d90c2558ec52f3 100644 (file)
--- a/helpers.h
+++ b/helpers.h
@@ -30,5 +30,6 @@ void warn(char *, ...);
 __attribute__((noreturn))
 void err(char *, ...);
 uint32_t get_color(char *);
+double distance(xcb_point_t, xcb_point_t);
 
 #endif
index 11f4a1ca9f6a2c616a44d3ec8889eedb21ff933e..71164c48888451b7325c3366ebf6a79146d669cc 100644 (file)
@@ -137,7 +137,7 @@ void process_message(char *msg, char *rsp)
         if (dir != NULL) {
             direction_t d;
             if (parse_direction(dir, &d))
-                swap_nodes(mon->desk->focus, find_neighbor(mon->desk->focus, d));
+                swap_nodes(mon->desk->focus, focus_by_distance ? nearest_neighbor(mon->desk, mon->desk->focus, d) : find_neighbor(mon->desk->focus, d));
         }
     } else if (strcmp(cmd, "toggle_fullscreen") == 0) {
         if (mon->desk->focus != NULL)
@@ -435,7 +435,11 @@ void process_message(char *msg, char *rsp)
         if (dir != NULL) {
             direction_t d;
             if (parse_direction(dir, &d)) {
-                node_t *n = find_neighbor(mon->desk->focus, d);
+                node_t *n;
+                if (focus_by_distance)
+                    n = nearest_neighbor(mon->desk, mon->desk->focus, d);
+                else
+                    n = find_neighbor(mon->desk->focus, d);
                 focus_node(mon, mon->desk, n, true);
             }
         }
@@ -519,7 +523,7 @@ void set_setting(char *name, char *value, char *rsp)
             uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK : CLIENT_EVENT_MASK_FFP)};
             for (monitor_t *m = mon_head; m != NULL; m = m->next)
                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-                    for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n))
+                    for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
                         xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
             if (focus_follows_pointer)
                 disable_motion_recorder();
@@ -539,6 +543,10 @@ void set_setting(char *name, char *value, char *rsp)
         bool b;
         if (parse_bool(value, &b))
             auto_alternate = b;
+    } else if (strcmp(name, "focus_by_distance") == 0) {
+        bool b;
+        if (parse_bool(value, &b))
+            focus_by_distance = b;
     } else if (strcmp(name, "wm_name") == 0) {
         strncpy(wm_name, value, sizeof(wm_name));
         ewmh_update_wm_name();
@@ -598,6 +606,8 @@ void get_setting(char *name, char* rsp)
         snprintf(rsp, BUFSIZ, "%s", BOOLSTR(apply_shadow_property));
     else if (strcmp(name, "auto_alternate") == 0)
         snprintf(rsp, BUFSIZ, "%s", BOOLSTR(auto_alternate));
+    else if (strcmp(name, "focus_by_distance") == 0)
+        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(focus_by_distance));
     else if (strcmp(name, "wm_name") == 0)
         snprintf(rsp, BUFSIZ, "%s", wm_name);
     else
index 465e0182834671289a137b994b40acf7789c2da5..7162f8b9d4e0511f31a33be915566772b923d6c3 100644 (file)
@@ -66,4 +66,5 @@ void load_settings(void)
     adaptative_raise = ADAPTATIVE_RAISE;
     apply_shadow_property = APPLY_SHADOW_PROPERTY;
     auto_alternate = AUTO_ALTERNATE;
+    focus_by_distance = FOCUS_BY_DISTANCE;
 }
index c4a4d1c07334429c00bbf800576a37e62d07212a..e53b9a60ac6dd968c578b46f839a4b2165c4d8de 100644 (file)
@@ -26,6 +26,7 @@
 #define ADAPTATIVE_RAISE       false
 #define APPLY_SHADOW_PROPERTY  false
 #define AUTO_ALTERNATE         false
+#define FOCUS_BY_DISTANCE      false
 
 char focused_border_color[MAXLEN];
 char active_border_color[MAXLEN];
@@ -55,6 +56,7 @@ bool focus_follows_pointer;
 bool adaptative_raise;
 bool apply_shadow_property;
 bool auto_alternate;
+bool focus_by_distance;
 
 char wm_name[MAXLEN];
 
diff --git a/tree.c b/tree.c
index 9cd4e077d53338c232ea8d7b03b628bfae8cc356..382de39327bb9fa1379cabfcb17fdae8165cea66 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -3,6 +3,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
+#include <float.h>
 #include <xcb/xcb.h>
 #include <xcb/xcb_event.h>
 #include "settings.h"
@@ -66,26 +67,26 @@ node_t *second_extrema(node_t *n)
         return second_extrema(n->second_child);
 }
 
-node_t *next_leaf(node_t *n)
+node_t *next_leaf(node_t *n, node_t *r)
 {
     if (n == NULL)
         return NULL;
     node_t *p = n;
-    while (is_second_child(p))
+    while (is_second_child(p) && p != r)
         p = p->parent;
-    if (p->parent == NULL)
+    if (p == r)
         return NULL;
     return first_extrema(p->parent->second_child);
 }
 
-node_t *prev_leaf(node_t *n)
+node_t *prev_leaf(node_t *n, node_t *r)
 {
     if (n == NULL)
         return NULL;
     node_t *p = n;
-    while (is_first_child(p))
+    while (is_first_child(p) && p != r)
         p = p->parent;
-    if (p->parent == NULL)
+    if (p == r)
         return NULL;
     return second_extrema(p->parent->first_child);
 }
@@ -126,6 +127,59 @@ node_t *find_neighbor(node_t *n, direction_t dir)
     return NULL;
 }
 
+void get_opposite(direction_t dir, direction_t* val)
+{
+    switch (dir) {
+        case DIR_RIGHT:
+            *val = DIR_LEFT;
+            break;
+        case DIR_DOWN:
+            *val = DIR_UP;
+            break;
+        case DIR_LEFT:
+            *val = DIR_RIGHT;
+            break;
+        case DIR_UP:
+            *val = DIR_DOWN;
+            break;
+    }
+}
+
+node_t *nearest_neighbor(desktop_t *d, node_t *n, direction_t dir)
+{
+    node_t *target = NULL;
+    if (is_tiled(n->client)) {
+        target = find_fence(n, dir);
+        if (target == NULL)
+            return NULL;
+        if (dir == DIR_UP || dir == DIR_LEFT)
+            target = target->first_child;
+        else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+            target = target->second_child;
+    } else {
+        target = d->root;
+    }
+    node_t *nearest = NULL;
+    direction_t dir2;
+    xcb_point_t pt;
+    xcb_point_t pt2;
+    get_side_handle(n->client, dir, &pt);
+    get_opposite(dir, &dir2);
+    double ds = DBL_MAX;
+    for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
+        if (is_tiled(a->client) != is_tiled(n->client) || a == n)
+            continue;
+        get_side_handle(a->client, dir2, &pt2);
+        double ds2 = distance(pt, pt2);
+        PRINTF("distance %X %g\n", a->client->window, ds2);
+        if (ds2 < ds) {
+            ds = ds2;
+            nearest = a;
+        }
+    }
+    return nearest;
+}
+
 int tiled_area(node_t *n)
 {
     if (n == NULL)
@@ -142,7 +196,7 @@ node_t *find_by_area(desktop_t *d, swap_arg_t a)
     node_t *r = NULL;
     int r_area = tiled_area(r);
 
-    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f)) {
+    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
         int f_area = tiled_area(f);
         if (r == NULL) {
             r = f;
@@ -675,14 +729,14 @@ void select_desktop(desktop_t *d)
 
         while (n != NULL) {
             window_show(n->client->window);
-            n = next_leaf(n);
+            n = next_leaf(n, d->root);
         }
 
         n = first_extrema(mon->desk->root);
 
         while (n != NULL) {
             window_hide(n->client->window);
-            n = next_leaf(n);
+            n = next_leaf(n, mon->desk->root);
         }
     }
 
@@ -728,7 +782,7 @@ void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_cli
 
     PUTS("cycle leaf");
 
-    node_t *f = (dir == CYCLE_PREV ? prev_leaf(n) : next_leaf(n));
+    node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
     if (f == NULL)
         f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
 
@@ -740,7 +794,7 @@ void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_cli
             focus_node(m, d, f, true);
             return;
         }
-        f = (dir == CYCLE_PREV ? prev_leaf(f) : next_leaf(f));
+        f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
         if (f == NULL)
             f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
     }
@@ -755,7 +809,7 @@ void nearest_leaf(monitor_t *m, desktop_t *d, node_t *n, nearest_arg_t dir, skip
 
     node_t *x = NULL;
 
-    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f))
+    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root))
         if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !is_tiled(f->client)) || (skip == CLIENT_SKIP_FLOATING && is_tiled(f->client))
                 || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
                 || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0))
@@ -776,10 +830,10 @@ void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir) {
     node_t *par = d->focus->parent;
     bool focus_first_child = is_first_child(d->focus);
     if (dir == CIRCULATE_FORWARD)
-        for (node_t *s = second_extrema(d->root), *f = prev_leaf(s); f != NULL; s = prev_leaf(f), f = prev_leaf(s))
+        for (node_t *s = second_extrema(d->root), *f = prev_leaf(s, d->root); f != NULL; s = prev_leaf(f, d->root), f = prev_leaf(s, d->root))
             swap_nodes(f, s);
     else
-        for (node_t *f = first_extrema(d->root), *s = next_leaf(f); s != NULL; f = next_leaf(s), s = next_leaf(f))
+        for (node_t *f = first_extrema(d->root), *s = next_leaf(f, d->root); s != NULL; f = next_leaf(s, d->root), s = next_leaf(f, d->root))
             swap_nodes(f, s);
     if (focus_first_child)
         focus_node(m, d, par->first_child, true);
@@ -828,7 +882,7 @@ void put_status(void)
     for (monitor_t *m = mon_head; m != NULL; m = m->next) {
         fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
-            for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n))
+            for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
                 urgent |= n->client->urgent;
             fprintf(status_fifo, "%c%s:", m->desk == d ? (urgent ? 'U' : 'D') : (d->root == NULL ? 'E' : (urgent ? 'u' : 'd')), d->name);
         }
@@ -1019,7 +1073,7 @@ void restore(char *file_path)
         client_uid = max_uid + 1;
         for (monitor_t *m = mon_head; m != NULL; m = m->next)
             for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-                for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n)) {
+                for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                     uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)};
                     xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
                     if (n->client->floating) {
diff --git a/tree.h b/tree.h
index 2bbde67d8356e5ebbfb75bf053cda27ad95d730a..775be3f89fef4c9ab25431c4c51ce3b703550e63 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -12,10 +12,12 @@ bool is_second_child(node_t *);
 void change_split_ratio(node_t *, value_change_t);
 node_t *first_extrema(node_t *);
 node_t *second_extrema(node_t *);
-node_t *next_leaf(node_t *);
-node_t *prev_leaf(node_t *);
+node_t *next_leaf(node_t *, node_t *);
+node_t *prev_leaf(node_t *, node_t *);
 node_t *find_fence(node_t *, direction_t);
 node_t *find_neighbor(node_t *, direction_t);
+void get_opposite(direction_t, direction_t*);
+node_t *nearest_neighbor(desktop_t *, node_t *, direction_t);
 int tiled_area(node_t *);
 node_t *find_by_area(desktop_t *, swap_arg_t);
 void move_fence(node_t *, direction_t, fence_move_t);
index a4292c1f5708af93e1e3c3a046510c5e076cab9c..ac9ea762e2c6d22f44685f29c62447567ce7941f 100644 (file)
--- a/window.c
+++ b/window.c
@@ -29,7 +29,7 @@ bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
 
 bool might_cover(desktop_t *d, node_t *n)
 {
-    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f))
+    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root))
         if (f != n && is_floating(f->client) && contains(n->client->floating_rectangle, f->client->floating_rectangle))
             return true;
     return false;
@@ -39,7 +39,7 @@ bool locate_window(xcb_window_t win, window_location_t *loc)
 {
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n))
+            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
                 if (n->client->window == win) {
                     loc->monitor = m;
                     loc->desktop = d;
@@ -68,6 +68,29 @@ bool is_inside(monitor_t *m, xcb_point_t pt)
             && r.y <= pt.y && pt.y < (r.y + r.height));
 }
 
+void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt)
+{
+    xcb_rectangle_t rect = (is_tiled(c) ? c->tiled_rectangle : c->floating_rectangle);
+    switch (dir) {
+        case DIR_RIGHT:
+            pt->x = rect.x + rect.width;
+            pt->y = rect.y + (rect.height / 2);
+            break;
+        case DIR_DOWN:
+            pt->x = rect.x + (rect.width / 2);
+            pt->y = rect.y + rect.height;
+            break;
+        case DIR_LEFT:
+            pt->x = rect.x;
+            pt->y = rect.y + (rect.height / 2);
+            break;
+        case DIR_UP:
+            pt->x = rect.x + (rect.width / 2);
+            pt->y = rect.y;
+            break;
+    }
+}
+
 monitor_t *monitor_from_point(xcb_point_t pt)
 {
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
@@ -392,7 +415,7 @@ void list_windows(char *rsp)
 
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n)) {
+            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                 snprintf(line, sizeof(line), "0x%X\n", n->client->window);
                 strncat(rsp, line, REMLEN(rsp));
             }
@@ -528,7 +551,7 @@ void toggle_visibility(void)
         clear_input_focus();
     visible = !visible;
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n))
+        for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
             window_set_visibility(n->client->window, visible);
     if (visible)
         update_current();
index 090864a2d2a493caf9e3bccd87a863237b6fce49..fc47250bd4e73ea27174a4e1a9e67b97a366dae1 100644 (file)
--- a/window.h
+++ b/window.h
@@ -12,6 +12,7 @@ bool might_cover(desktop_t *, node_t *);
 bool locate_window(xcb_window_t, window_location_t *);
 bool locate_desktop(char *, desktop_location_t *);
 bool is_inside(monitor_t *, xcb_point_t);
+void get_side_handle(client_t *, direction_t, xcb_point_t *);
 monitor_t *monitor_from_point(xcb_point_t);
 monitor_t *underlying_monitor(client_t *);
 void manage_window(monitor_t *, desktop_t *, xcb_window_t);