- `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`.
{
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=()
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
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)
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include "bspwm.h"
return pxl;
}
+
+double distance(xcb_point_t a, xcb_point_t b)
+{
+ return hypot(a.x - b.x, a.y - b.y);
+}
__attribute__((noreturn))
void err(char *, ...);
uint32_t get_color(char *);
+double distance(xcb_point_t, xcb_point_t);
#endif
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)
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);
}
}
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();
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();
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
adaptative_raise = ADAPTATIVE_RAISE;
apply_shadow_property = APPLY_SHADOW_PROPERTY;
auto_alternate = AUTO_ALTERNATE;
+ focus_by_distance = FOCUS_BY_DISTANCE;
}
#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];
bool adaptative_raise;
bool apply_shadow_property;
bool auto_alternate;
+bool focus_by_distance;
char wm_name[MAXLEN];
#include <string.h>
#include <ctype.h>
#include <math.h>
+#include <float.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include "settings.h"
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);
}
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)
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;
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);
}
}
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));
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));
}
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))
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);
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);
}
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) {
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);
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;
{
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;
&& 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)
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));
}
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();
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);