VERSION = 0.1
CC = gcc
-LIBS = -lm -lxcb -lxcb-icccm -lxcb-ewmh
+LIBS = -lm -lxcb -lxcb-icccm -lxcb-ewmh -lxcb-xinerama
CFLAGS = -std=c99 -pedantic -Wall -Wextra
LDFLAGS = $(LIBS)
dump
Output the internal representation of the window tree.
- list
- Perform a dump of each desktop.
+ list_monitors [--quiet]
+ Perform a dump of each monitor.
+
+ list [--quiet]
+ Perform a dump of each desktop for the current monitor.
windows
Return the list of managed windows (i.e. their identifiers).
send_to DESKTOP_NAME
Send the focused window to the given desktop.
+ use_monitor MONITOR_NAME
+ Select the given monitor.
+
use DESKTOP_NAME
Select the given desktop.
+ alternate_monitor
+ Alternate between the current and the last focused monitor.
+
alternate
Alternate between the current and the last focused desktop.
add DESKTOP_NAME
Make a new desktop with the given name.
+ rename_monitor CURRENT_NAME NEW_NAME
+ Rename the monitor named CURRENT_NAME to NEW_NAME.
+
rename CURRENT_NAME NEW_NAME
Rename the desktop named CURRENT_NAME to NEW_NAME.
+ cycle_monitor CYC
+ Select the next or previous monitor.
+
cycle_desktop CYC
Select the next or previous desktop.
Colors are either [X color names](http://en.wikipedia.org/wiki/X11_color_names) or '#RRGGBB', booleans are 'true' or 'false'.
+ focused_border_color
+ Color of the main border of a focused window of a focused monitor.
+
active_border_color
- Color of the main border of a focused window.
+ Color of the main border of a focused window of an unfocused monitor.
normal_border_color
Color of the main border of an unfocused window.
presel_border_color
Color of the *presel* message feedback.
+ focused_locked_border_color
+ Color of the main border of a focused locked window of a focused monitor.
+
active_locked_border_color
- Color of the main border of a focused locked window.
+ Color of the main border of a focused locked window of an unfocused monitor.
normal_locked_border_color
Color of the main border of an unfocused locked window.
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_ewmh.h>
+#include <xcb/xinerama.h>
#include "types.h"
#include "settings.h"
#include "messages.h"
xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
- desk = make_desktop(DEFAULT_DESK_NAME);
- last_desk = NULL;
- desk_head = desk;
- desk_tail = desk;
- num_desktops++;
+ monitor_uid = desktop_uid = 0;
+ mon = last_mon = mon_head = mon_tail = NULL;
+
+ bool xinerama_is_active = false;
+
+ if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
+ xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
+ if (xia != NULL) {
+ xinerama_is_active = xia->state;
+ free(xia);
+ }
+ }
+
+ if (xinerama_is_active) {
+ xcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);
+ xcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);
+ int n = xcb_xinerama_query_screens_screen_info_length(xsq);
+ PRINTF("number of monitors: %d\n", n);
+ for (int i = 0; i < n; i++) {
+ xcb_xinerama_screen_info_t info = xsi[i];
+ xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
+ add_monitor(&rect);
+ }
+ free(xsq);
+ } else {
+ warn("Xinerama is inactive");
+ xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+ add_monitor(&rect);
+ }
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next)
+ add_desktop(m, NULL);
ewmh_update_number_of_desktops();
ewmh_update_desktop_names();
xcb_connection_t *dpy;
int default_screen, screen_width, screen_height;
-unsigned int num_clients;
+uint32_t num_clients;
uint32_t num_desktops;
+unsigned int num_monitors;
+unsigned int monitor_uid;
+unsigned int desktop_uid;
xcb_screen_t *screen;
xcb_rectangle_t root_rect;
uint8_t root_depth;
split_mode_t split_mode;
direction_t split_dir;
-desktop_t *desk;
-desktop_t *last_desk;
-desktop_t *desk_head;
-desktop_t *desk_tail;
+monitor_t *mon;
+monitor_t *last_mon;
+monitor_t *mon_head;
+monitor_t *mon_tail;
rule_t *rule_head;
pointer_state_t *frozen_pointer;
return;
}
+ desktop_t *desk = mon->desk;
client_t *c = make_client(win);
update_floating_rectangle(c);
c->transient = transient;
if (takes_focus)
- focus_node(desk, birth, false);
-
- apply_layout(desk, desk->root, root_rect);
+ focus_node(mon, desk, birth, false);
+ fit_monitor(mon, birth->client);
+ arrange(mon, desk);
window_show(c->window);
if (takes_focus)
xcb_configure_window(dpy, e->window, mask, values);
if (is_managed)
- window_draw_border(loc.node, (loc.node == loc.desktop->focus));
+ window_draw_border(loc.node, loc.node == loc.desktop->focus, loc.monitor == mon);
} else {
xcb_configure_notify_event_t evt;
xcb_rectangle_t rect;
window_location_t loc;
if (locate_window(e->window, &loc)) {
remove_node(loc.desktop, loc.node);
- apply_layout(loc.desktop, loc.desktop->root, root_rect);
+ arrange(loc.monitor, loc.desktop);
}
}
window_location_t loc;
if (locate_window(e->window, &loc)) {
remove_node(loc.desktop, loc.node);
- apply_layout(loc.desktop, loc.desktop->root, root_rect);
+ arrange(loc.monitor, loc.desktop);
}
}
return;
if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1) {
loc.node->client->urgent = (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY);
- if (desk == loc.desktop)
- apply_layout(loc.desktop, loc.desktop->root, root_rect);
+ if (loc.monitor->desk == loc.desktop)
+ arrange(loc.monitor, loc.desktop);
}
}
}
} else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node)
toggle_fullscreen(loc.desktop->focus->client);
- if (desk != loc.desktop) {
- apply_layout(loc.desktop, loc.desktop->root, root_rect);
+ if (loc.monitor->desk != loc.desktop) {
+ arrange(loc.monitor, loc.desktop);
select_desktop(loc.desktop);
}
- focus_node(loc.desktop, loc.node, true);
+ focus_node(loc.monitor, loc.desktop, loc.node, true);
}
}
client_t *c = loc.node->client;
switch (e->detail) {
case XCB_BUTTON_INDEX_2:
- focus_node(loc.desktop, loc.node, true);
+ focus_node(loc.monitor, loc.desktop, loc.node, true);
break;
case XCB_BUTTON_INDEX_1:
case XCB_BUTTON_INDEX_3:
int16_t delta_x, delta_y, x, y, w, h;
uint16_t width, height;
+ monitor_t *m = frozen_pointer->monitor;
desktop_t *d = frozen_pointer->desktop;
node_t *n = frozen_pointer->node;
client_t *c = n->client;
height = MAX(1, h);
window_move_resize(win, x, y, width, height);
c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
- window_draw_border(n, (d->focus == n));
+ window_draw_border(n, d->focus == n, mon == m);
}
}
|| (fs && action == XCB_EWMH_WM_STATE_REMOVE)
|| (!fs && action == XCB_EWMH_WM_STATE_ADD)) {
toggle_fullscreen(n->client);
- apply_layout(desk, desk->root, root_rect);
+ arrange(mon, mon->desk);
}
}
}
void ewmh_update_active_window(void)
{
- xcb_window_t win = (desk->focus == NULL ? XCB_NONE : desk->focus->client->window);
+ xcb_window_t win = (mon->desk->focus == NULL ? XCB_NONE : mon->desk->focus->client->window);
xcb_ewmh_set_active_window(ewmh, default_screen, win);
}
uint32_t ewmh_get_desktop_index(desktop_t *d)
{
- desktop_t *cd = desk_head;
- uint32_t i;
+ uint32_t i = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next)
+ for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++)
+ if (d == cd)
+ return i;
- for (i = 0; cd != NULL && i < num_desktops; i++, cd = cd->next) {
- if (d == cd)
- break;
- }
-
- return i;
+ return 0;
}
void ewmh_update_current_desktop(void)
{
- uint32_t i = ewmh_get_desktop_index(desk);
- xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
+ uint32_t i = ewmh_get_desktop_index(mon->desk);
+ xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
}
void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
void ewmh_update_desktop_names(void)
{
- char names[MAXLEN];
- desktop_t *d = desk_head;
- unsigned int pos, i;
-
- pos = i = 0;
+ char names[MAXLEN];
+ monitor_t *m = mon_head;
+ unsigned int pos, i;
+ pos = i = 0;
+
+ while (m != NULL) {
+ desktop_t *d = m->desk_head;
+
+ while (d != NULL && i < num_desktops) {
+ for (unsigned int j = 0; j < strlen(d->name); j++)
+ names[pos + j] = d->name[j];
+ pos += strlen(d->name);
+ names[pos] = '\0';
+ pos++;
+ d = d->next;
+ i++;
+ }
- while (d != NULL && i < num_desktops) {
- for (unsigned int j = 0; j < strlen(d->name); j++)
- names[pos + j] = d->name[j];
- pos += strlen(d->name);
- names[pos] = '\0';
- pos++;
- d = d->next;
- i++;
- }
+ m = m->next;
+ }
- if (i != num_desktops)
- return;
+ if (i != num_desktops)
+ return;
- pos--;
+ pos--;
- xcb_ewmh_set_desktop_names(ewmh, default_screen, pos, names);
+ xcb_ewmh_set_desktop_names(ewmh, default_screen, pos, names);
}
void ewmh_update_client_list(void)
}
xcb_window_t wins[num_clients];
- desktop_t *d = desk_head;
unsigned int i = 0;
- while (d != NULL && i < num_clients) {
- node_t *n = first_extrema(d->root);
- while (n != NULL) {
- wins[i++] = n->client->window;
- n = next_leaf(n);
- }
- d = d->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))
+ wins[i++] = n->client->window;
if (i != num_clients)
return;
set_setting(name, value, rsp);
return;
} else if (strcmp(cmd, "dump") == 0) {
- dump_tree(desk, desk->root, rsp, 0);
+ dump_tree(mon->desk, mon->desk->root, rsp, 0);
+ return;
+ } else if (strcmp(cmd, "list_monitors") == 0) {
+ char *arg = strtok(NULL, TOKEN_SEP);
+ list_option_t opt;
+ if (parse_list_option(arg, &opt))
+ list_monitors(opt, rsp);
return;
} else if (strcmp(cmd, "list") == 0) {
- list_desktops(rsp);
+ char *arg = strtok(NULL, TOKEN_SEP);
+ list_option_t opt;
+ if (parse_list_option(arg, &opt))
+ list_desktops(mon, opt, 0, rsp);
return;
} else if (strcmp(cmd, "windows") == 0) {
list_windows(rsp);
return;
} else if (strcmp(cmd, "close") == 0) {
- window_close(desk->focus);
+ window_close(mon->desk->focus);
return;
} else if (strcmp(cmd, "kill") == 0) {
- window_kill(desk, desk->focus);
+ window_kill(mon->desk, mon->desk->focus);
} else if (strcmp(cmd, "magnetise") == 0) {
char *cor = strtok(NULL, TOKEN_SEP);
if (cor != NULL) {
corner_t c;
if (parse_corner(cor, &c)) {
- magnetise_tree(desk->root, c);
+ magnetise_tree(mon->desk->root, c);
}
}
} else if (strcmp(cmd, "rotate") == 0) {
if (deg != NULL) {
rotate_t r;
if (parse_rotate(deg, &r)) {
- rotate_tree(desk->root, r);
+ rotate_tree(mon->desk->root, r);
}
}
} else if (strcmp(cmd, "layout") == 0) {
if (lyt != NULL) {
layout_t l;
if (parse_layout(lyt, &l)) {
- desk->layout = l;
+ mon->desk->layout = l;
}
}
} else if (strcmp(cmd, "cycle_layout") == 0) {
- if (desk->layout == LAYOUT_MONOCLE)
- desk->layout = LAYOUT_TILED;
+ if (mon->desk->layout == LAYOUT_MONOCLE)
+ mon->desk->layout = LAYOUT_TILED;
else
- desk->layout = LAYOUT_MONOCLE;
+ mon->desk->layout = LAYOUT_MONOCLE;
} else if (strcmp(cmd, "shift") == 0) {
char *dir = strtok(NULL, TOKEN_SEP);
if (dir != NULL) {
direction_t d;
if (parse_direction(dir, &d)) {
- swap_nodes(desk->focus, find_neighbor(desk->focus, d));
+ swap_nodes(mon->desk->focus, find_neighbor(mon->desk->focus, d));
}
}
} else if (strcmp(cmd, "toggle_fullscreen") == 0) {
- if (desk->focus != NULL)
- toggle_fullscreen(desk->focus->client);
+ if (mon->desk->focus != NULL)
+ toggle_fullscreen(mon->desk->focus->client);
} else if (strcmp(cmd, "toggle_floating") == 0) {
split_mode = MODE_AUTOMATIC;
- toggle_floating(desk->focus);
+ toggle_floating(mon->desk->focus);
} else if (strcmp(cmd, "toggle_locked") == 0) {
- if (desk->focus != NULL)
- toggle_locked(desk->focus->client);
+ if (mon->desk->focus != NULL)
+ toggle_locked(mon->desk->focus->client);
} else if (strcmp(cmd, "ratio") == 0) {
char *value = strtok(NULL, TOKEN_SEP);
- if (value != NULL && desk->focus != NULL)
- sscanf(value, "%lf", &desk->focus->split_ratio);
+ if (value != NULL && mon->desk->focus != NULL)
+ sscanf(value, "%lf", &mon->desk->focus->split_ratio);
} else if (strcmp(cmd, "cancel") == 0) {
split_mode = MODE_AUTOMATIC;
- window_draw_border(desk->focus, true);
+ window_draw_border(mon->desk->focus, true, true);
return;
} else if (strcmp(cmd, "presel") == 0) {
- if (desk->focus == NULL || !is_tiled(desk->focus->client) || desk->layout != LAYOUT_TILED)
+ if (mon->desk->focus == NULL || !is_tiled(mon->desk->focus->client) || mon->desk->layout != LAYOUT_TILED)
return;
char *dir = strtok(NULL, TOKEN_SEP);
if (dir != NULL) {
if (parse_direction(dir, &d)) {
split_mode = MODE_MANUAL;
split_dir = d;
- window_draw_border(desk->focus, true);
+ window_draw_border(mon->desk->focus, true, true);
}
}
return;
fence_move_t m;
direction_t d;
if (parse_fence_move(cmd, &m) && parse_direction(dir, &d)) {
- move_fence(desk->focus, d, m);
+ move_fence(mon->desk->focus, d, m);
+ }
+ }
+ } else if (strcmp(cmd, "send_to_monitor") == 0) {
+ char *name = strtok(NULL, TOKEN_SEP);
+ if (name != NULL) {
+ desktop_location_t loc;
+ if (locate_desktop(name, &loc)) {
+ transfer_node(mon, mon->desk, loc.monitor, loc.desktop, mon->desk->focus);
+ if (mon != loc.monitor && loc.monitor->desk == loc.desktop)
+ arrange(loc.monitor, loc.desktop);
}
}
} else if (strcmp(cmd, "send_to") == 0) {
char *name = strtok(NULL, TOKEN_SEP);
if (name != NULL) {
- desktop_t *d = find_desktop(name);
- transfer_node(desk, d, desk->focus);
+ desktop_location_t loc;
+ if (locate_desktop(name, &loc)) {
+ transfer_node(mon, mon->desk, loc.monitor, loc.desktop, mon->desk->focus);
+ if (mon != loc.monitor && loc.monitor->desk == loc.desktop)
+ arrange(loc.monitor, loc.desktop);
+ }
+ }
+ } else if (strcmp(cmd, "rename_monitor") == 0) {
+ char *cur_name = strtok(NULL, TOKEN_SEP);
+ if (cur_name != NULL) {
+ monitor_t *m = find_monitor(cur_name);
+ if (m != NULL) {
+ char *new_name = strtok(NULL, TOKEN_SEP);
+ if (new_name != NULL) {
+ strncpy(m->name, new_name, sizeof(m->name));
+ }
+ }
}
} else if (strcmp(cmd, "rename") == 0) {
char *cur_name = strtok(NULL, TOKEN_SEP);
if (cur_name != NULL) {
- desktop_t *d = find_desktop(cur_name);
- if (d != NULL) {
+ desktop_location_t loc;
+ if (locate_desktop(cur_name, &loc)) {
char *new_name = strtok(NULL, TOKEN_SEP);
if (new_name != NULL) {
- strncpy(d->name, new_name, sizeof(d->name));
+ strncpy(loc.desktop->name, new_name, sizeof(loc.desktop->name));
ewmh_update_desktop_names();
}
}
}
+ } else if (strcmp(cmd, "use_monitor") == 0) {
+ char *name = strtok(NULL, TOKEN_SEP);
+ if (name != NULL) {
+ monitor_t *m = find_monitor(name);
+ if (m != NULL)
+ select_monitor(m);
+ }
} else if (strcmp(cmd, "use") == 0) {
char *name = strtok(NULL, TOKEN_SEP);
if (name != NULL) {
- desktop_t *d = find_desktop(name);
- select_desktop(d);
+ desktop_location_t loc;
+ if (locate_desktop(name, &loc)) {
+ select_monitor(loc.monitor);
+ select_desktop(loc.desktop);
+ }
+ }
+ } else if (strcmp(cmd, "cycle_monitor") == 0) {
+ char *dir = strtok(NULL, TOKEN_SEP);
+ if (dir != NULL) {
+ cycle_dir_t d;
+ if (parse_cycle_direction(dir, &d))
+ cycle_monitor(d);
}
} else if (strcmp(cmd, "cycle_desktop") == 0) {
char *dir = strtok(NULL, TOKEN_SEP);
if (dir != NULL) {
cycle_dir_t d;
- if (parse_cycle_direction(dir, &d)) {
+ if (parse_cycle_direction(dir, &d))
cycle_desktop(d);
- }
}
} else if (strcmp(cmd, "cycle") == 0) {
- if (desk->focus != NULL && desk->focus->client->fullscreen)
+ if (mon->desk->focus != NULL && mon->desk->focus->client->fullscreen)
return;
char *dir = strtok(NULL, TOKEN_SEP);
if (dir != NULL) {
skip_client_t k;
char *skip = strtok(NULL, TOKEN_SEP);
if (parse_skip_client(skip, &k))
- cycle_leaf(desk, desk->focus, d, k);
+ cycle_leaf(d, k);
}
}
return;
rule_head = rule;
}
return;
+ } else if (strcmp(cmd, "alternate_monitor") == 0) {
+ select_monitor(last_mon);
} else if (strcmp(cmd, "alternate") == 0) {
- select_desktop(last_desk);
+ select_desktop(mon->last_desk);
} else if (strcmp(cmd, "add") == 0) {
char *name = strtok(NULL, TOKEN_SEP);
if (name != NULL) {
- add_desktop(name);
+ add_desktop(mon, name);
}
return;
} else if (strcmp(cmd, "focus") == 0) {
- if (desk->focus != NULL && desk->focus->client->fullscreen)
+ if (mon->desk->focus != NULL && mon->desk->focus->client->fullscreen)
return;
char *dir = strtok(NULL, TOKEN_SEP);
if (dir != NULL) {
direction_t d;
if (parse_direction(dir, &d)) {
- node_t *n = find_neighbor(desk->focus, d);
- focus_node(desk, n, true);
+ node_t *n = find_neighbor(mon->desk->focus, d);
+ focus_node(mon, mon->desk, n, true);
}
}
return;
return;
}
- apply_layout(desk, desk->root, root_rect);
+ arrange(mon, mon->desk);
}
void set_setting(char *name, char *value, char *rsp)
} else if (strcmp(name, "bottom_padding") == 0) {
sscanf(value, "%i", &bottom_padding);
update_root_dimensions();
+ } else if (strcmp(name, "focused_border_color") == 0) {
+ strncpy(focused_border_color, value, sizeof(focused_border_color));
+ focused_border_color_pxl = get_color(focused_border_color);
} else if (strcmp(name, "active_border_color") == 0) {
strncpy(active_border_color, value, sizeof(active_border_color));
active_border_color_pxl = get_color(active_border_color);
} else if (strcmp(name, "presel_border_color") == 0) {
strncpy(presel_border_color, value, sizeof(presel_border_color));
presel_border_color_pxl = get_color(presel_border_color);
+ } else if (strcmp(name, "focused_locked_border_color") == 0) {
+ strncpy(focused_locked_border_color, value, sizeof(focused_locked_border_color));
+ focused_locked_border_color_pxl = get_color(focused_locked_border_color);
} else if (strcmp(name, "active_locked_border_color") == 0) {
strncpy(active_locked_border_color, value, sizeof(active_locked_border_color));
active_locked_border_color_pxl = get_color(active_locked_border_color);
return;
}
- apply_layout(desk, desk->root, root_rect);
+ arrange(mon, mon->desk);
}
void get_setting(char *name, char* rsp)
snprintf(rsp, BUFSIZ, "%i", top_padding);
else if (strcmp(name, "bottom_padding") == 0)
snprintf(rsp, BUFSIZ, "%i", bottom_padding);
+ else if (strcmp(name, "focused_border_color") == 0)
+ snprintf(rsp, BUFSIZ, "%s (%06X)", focused_border_color, focused_border_color_pxl);
else if (strcmp(name, "active_border_color") == 0)
snprintf(rsp, BUFSIZ, "%s (%06X)", active_border_color, active_border_color_pxl);
else if (strcmp(name, "normal_border_color") == 0)
snprintf(rsp, BUFSIZ, "%s (%06X)", outer_border_color, outer_border_color_pxl);
else if (strcmp(name, "presel_border_color") == 0)
snprintf(rsp, BUFSIZ, "%s (%06X)", presel_border_color, presel_border_color_pxl);
+ else if (strcmp(name, "focused_locked_border_color") == 0)
+ snprintf(rsp, BUFSIZ, "%s (%06X)", focused_locked_border_color, focused_locked_border_color_pxl);
else if (strcmp(name, "active_locked_border_color") == 0)
snprintf(rsp, BUFSIZ, "%s (%06X)", active_locked_border_color, active_locked_border_color_pxl);
else if (strcmp(name, "normal_locked_border_color") == 0)
snprintf(rsp, BUFSIZ, "unknown setting: %s", name);
}
-
bool parse_bool(char *value, bool *b)
{
if (strcmp(value, "true") == 0) {
return false;
}
+bool parse_list_option(char *s, list_option_t *o)
+{
+ if (s == NULL || strcmp(s, "--verbose") == 0) {
+ *o = LIST_OPTION_VERBOSE;
+ return true;
+ } else if (strcmp(s, "--quiet") == 0) {
+ *o = LIST_OPTION_QUIET;
+ return true;
+ }
+ return false;
+}
+
bool parse_corner(char *s, corner_t *c)
{
if (strcmp(s, "top_left") == 0) {
}
return false;
}
-
bool parse_layout(char *, layout_t *);
bool parse_direction(char *, direction_t *);
bool parse_cycle_direction(char *, cycle_dir_t *);
+bool parse_list_option(char *, list_option_t *);
bool parse_skip_client(char *, skip_client_t *);
bool parse_corner(char *, corner_t *);
bool parse_rotate(char *, rotate_t *);
void load_settings(void)
{
strncpy(normal_border_color, NORMAL_BORDER_COLOR, sizeof(normal_border_color));
+ strncpy(focused_border_color, FOCUSED_BORDER_COLOR, sizeof(focused_border_color));
strncpy(active_border_color, ACTIVE_BORDER_COLOR, sizeof(active_border_color));
strncpy(inner_border_color, INNER_BORDER_COLOR, sizeof(inner_border_color));
strncpy(outer_border_color, OUTER_BORDER_COLOR, sizeof(outer_border_color));
strncpy(presel_border_color, PRESEL_BORDER_COLOR, sizeof(presel_border_color));
+ strncpy(focused_locked_border_color, FOCUSED_LOCKED_BORDER_COLOR, sizeof(focused_locked_border_color));
strncpy(active_locked_border_color, ACTIVE_LOCKED_BORDER_COLOR, sizeof(active_locked_border_color));
strncpy(normal_locked_border_color, NORMAL_LOCKED_BORDER_COLOR, sizeof(normal_locked_border_color));
strncpy(urgent_border_color, URGENT_BORDER_COLOR, sizeof(urgent_border_color));
normal_border_color_pxl = get_color(normal_border_color);
+ focused_border_color_pxl = get_color(active_border_color);
active_border_color_pxl = get_color(active_border_color);
inner_border_color_pxl = get_color(inner_border_color);
outer_border_color_pxl = get_color(outer_border_color);
presel_border_color_pxl = get_color(presel_border_color);
+ focused_locked_border_color_pxl = get_color(active_locked_border_color);
active_locked_border_color_pxl = get_color(active_locked_border_color);
normal_locked_border_color_pxl = get_color(normal_locked_border_color);
urgent_border_color_pxl = get_color(urgent_border_color);
#define WM_NAME "bspwm"
#define AUTOSTART_FILE "autostart"
+#define FOCUSED_BORDER_COLOR "#7D7F8A"
#define ACTIVE_BORDER_COLOR "#7D7F8A"
#define NORMAL_BORDER_COLOR "#3F3E3B"
#define INNER_BORDER_COLOR "#32312E"
#define OUTER_BORDER_COLOR "#32312E"
#define PRESEL_BORDER_COLOR "#97AE71"
+#define FOCUSED_LOCKED_BORDER_COLOR "#B6A56A"
#define ACTIVE_LOCKED_BORDER_COLOR "#B6A56A"
#define NORMAL_LOCKED_BORDER_COLOR "#8D7E45"
#define URGENT_BORDER_COLOR "#DE928B"
#define BORDERLESS_MONOCLE false
+char focused_border_color[MAXLEN];
char active_border_color[MAXLEN];
char normal_border_color[MAXLEN];
char inner_border_color[MAXLEN];
char outer_border_color[MAXLEN];
char presel_border_color[MAXLEN];
+char focused_locked_border_color[MAXLEN];
char active_locked_border_color[MAXLEN];
char normal_locked_border_color[MAXLEN];
char urgent_border_color[MAXLEN];
+uint32_t focused_border_color_pxl;
uint32_t active_border_color_pxl;
uint32_t normal_border_color_pxl;
uint32_t inner_border_color_pxl;
uint32_t outer_border_color_pxl;
uint32_t presel_border_color_pxl;
+uint32_t focused_locked_border_color_pxl;
uint32_t active_locked_border_color_pxl;
uint32_t normal_locked_border_color_pxl;
uint32_t urgent_border_color_pxl;
magnetise_tree(n->second_child, corner);
}
-void dump_tree(desktop_t *d, node_t *n, char *rsp, int depth)
+void dump_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
{
if (n == NULL)
return;
char line[MAXLEN];
- for (int i = 0; i < depth; i++)
+ for (unsigned int i = 0; i < depth; i++)
strncat(rsp, " ", REMLEN(rsp));
if (is_leaf(n))
}
void refresh_current(void) {
- if (desk->focus == NULL)
+ if (mon->desk->focus == NULL)
ewmh_update_active_window();
else
- focus_node(desk, desk->focus, true);
+ focus_node(mon, mon->desk, mon->desk->focus, true);
}
-void list_desktops(char *rsp)
+void list_monitors(list_option_t opt, char *rsp)
{
- desktop_t *d = desk_head;
+ monitor_t *m = mon_head;
+
+ while (m != NULL) {
+ strncat(rsp, m->name, REMLEN(rsp));
+ if (mon == m)
+ strncat(rsp, " #\n", REMLEN(rsp));
+ else
+ strncat(rsp, "\n", REMLEN(rsp));
+ if (opt == LIST_OPTION_VERBOSE)
+ list_desktops(m, opt, 1, rsp);
+ m = m->next;
+ }
+}
+
+void list_desktops(monitor_t *m, list_option_t opt, unsigned int depth, char *rsp)
+{
+ desktop_t *d = m->desk_head;
while (d != NULL) {
+ for (unsigned int i = 0; i < depth; i++)
+ strncat(rsp, " ", REMLEN(rsp));
strncat(rsp, d->name, REMLEN(rsp));
- if (desk == d)
+ if (m->desk == d)
strncat(rsp, " @\n", REMLEN(rsp));
else
strncat(rsp, "\n", REMLEN(rsp));
- dump_tree(d, d->root, rsp, 1);
+ if (opt == LIST_OPTION_VERBOSE)
+ dump_tree(d, d->root, rsp, depth + 1);
d = d->next;
}
}
root_rect.height = screen_height - (top_padding + bottom_padding + window_gap);
}
-void apply_layout(desktop_t *d, node_t *n, xcb_rectangle_t rect)
+void arrange(monitor_t *m, desktop_t *d)
{
- if (d == NULL || n == NULL)
+ xcb_rectangle_t rect = m->rectangle;
+ rect.x += left_padding + window_gap;
+ rect.y += top_padding + window_gap;
+ rect.width -= left_padding + right_padding + window_gap;
+ rect.height -= top_padding + bottom_padding + window_gap;
+ apply_layout(m, d, d->root, rect);
+}
+
+void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect)
+{
+ if (n == NULL)
return;
n->rectangle = rect;
window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
window_border_width(n->client->window, n->client->border_width);
- window_draw_border(n, n == d->focus);
+ window_draw_border(n, n == d->focus, m == mon);
if (d->layout == LAYOUT_MONOCLE && n == d->focus)
window_raise(n->client->window);
}
}
- apply_layout(d, n->first_child, first_rect);
- apply_layout(d, n->second_child, second_rect);
+ apply_layout(m, d, n->first_child, first_rect);
+ apply_layout(m, d, n->second_child, second_rect);
}
}
}
}
-void focus_node(desktop_t *d, node_t *n, bool is_mapped)
+void focus_node(monitor_t *m, desktop_t *d, node_t *n, bool is_mapped)
{
if (n == NULL)
return;
n->client->urgent = false;
if (is_mapped) {
- if (d->focus != n) {
- window_draw_border(d->focus, false);
- window_draw_border(n, true);
+ if (mon != m || d->focus != n) {
+ window_draw_border(d->focus, m != mon, m == mon);
+ window_draw_border(n, true, true);
}
xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
}
d->focus = n;
}
- if (d == desk)
- ewmh_update_active_window();
+ ewmh_update_active_window();
}
void update_current(void)
{
- if (desk->focus == NULL)
+ if (mon->desk->focus == NULL)
ewmh_update_active_window();
else
- focus_node(desk, desk->focus, true);
+ focus_node(mon, mon->desk, mon->desk->focus, true);
}
void unlink_node(desktop_t *d, node_t *n)
num_clients--;
ewmh_update_client_list();
- if (desk == d)
+ if (mon->desk == d)
update_current();
}
}
}
-void transfer_node(desktop_t *ds, desktop_t *dd, node_t *n)
+void fit_monitor(monitor_t *m, client_t *c)
+{
+ xcb_rectangle_t cr = c->floating_rectangle;
+ xcb_rectangle_t mr = m->rectangle;
+ if (cr.x < mr.x)
+ cr.x += mr.x;
+ if (cr.y < mr.y)
+ cr.y += mr.y;
+}
+
+void translate_coordinates(monitor_t *ms, monitor_t *md, client_t *c)
+{
+ if (ms == md)
+ return;
+
+ uint32_t vx = md->rectangle.x - ms->rectangle.x;
+ uint32_t vy = md->rectangle.y - ms->rectangle.y;
+ c->floating_rectangle.x += vx;
+ c->floating_rectangle.y += vy;
+}
+
+void transfer_node(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)
{
- if (n == NULL || ds == NULL || dd == NULL || dd == ds)
+ if (n == NULL || ds == NULL || dd == NULL || ms == NULL || md == NULL || (ms == md && dd == ds))
return;
PRINTF("transfer node %X\n", n->client->window);
insert_node(dd, n);
ewmh_set_wm_desktop(n, dd);
- if (ds == desk) {
+ if (ds == ms->desk && dd != md->desk) {
window_hide(n->client->window);
}
- if (dd == desk) {
+ translate_coordinates(ms, md, n->client);
+ if (n->client->fullscreen)
+ window_move_resize(n->client->window, md->rectangle.x, md->rectangle.y, md->rectangle.width, md->rectangle.height);
+
+ if (ds != ms->desk && dd == md->desk) {
window_show(n->client->window);
- focus_node(dd, n, true);
+ focus_node(md, dd, n, true);
} else {
- focus_node(dd, n, false);
+ focus_node(md, dd, n, false);
}
- if (ds == desk || dd == desk)
+ if (ds == ms->desk || dd == md->desk)
update_current();
}
+void select_monitor(monitor_t *m)
+{
+ if (m == NULL || mon == m)
+ return;
+
+ PRINTF("select monitor %s\n", m->name);
+
+ focus_node(m, m->desk, m->desk->focus, true);
+
+ last_mon = mon;
+ mon = m;
+
+ ewmh_update_current_desktop();
+}
+
void select_desktop(desktop_t *d)
{
- if (d == NULL || d == desk)
+ if (d == NULL || d == mon->desk)
return;
PRINTF("select desktop %s\n", d->name);
n = next_leaf(n);
}
- n = first_extrema(desk->root);
+ n = first_extrema(mon->desk->root);
while (n != NULL) {
window_hide(n->client->window);
n = next_leaf(n);
}
- last_desk = desk;
- desk = d;
+ mon->last_desk = mon->desk;
+ mon->desk = d;
update_current();
ewmh_update_current_desktop();
}
+void cycle_monitor(cycle_dir_t dir)
+{
+ if (dir == CYCLE_NEXT)
+ select_monitor((mon->next == NULL ? mon_head : mon->next));
+ else if (dir == CYCLE_PREV)
+ select_monitor((mon->prev == NULL ? mon_tail : mon->prev));
+}
+
void cycle_desktop(cycle_dir_t dir)
{
if (dir == CYCLE_NEXT)
- select_desktop((desk->next == NULL ? desk_head : desk->next));
+ select_desktop((mon->desk->next == NULL ? mon->desk_head : mon->desk->next));
else if (dir == CYCLE_PREV)
- select_desktop((desk->prev == NULL ? desk_tail : desk->prev));
+ select_desktop((mon->desk->prev == NULL ? mon->desk_tail : mon->desk->prev));
}
-void cycle_leaf(desktop_t *d, node_t *n, cycle_dir_t dir, skip_client_t skip)
+void cycle_leaf(cycle_dir_t dir, skip_client_t skip)
{
+ desktop_t *d = mon->desk;
+ node_t *n = mon->desk->focus;
+
if (n == NULL)
return;
if (skip == SKIP_NONE || (skip == SKIP_TILED && !tiled) || (skip == SKIP_FLOATING && tiled)
|| (skip == SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
|| (skip == SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) {
- focus_node(d, f, true);
+ focus_node(mon, d, f, true);
return;
}
f = (dir == CYCLE_PREV ? prev_leaf(f) : next_leaf(f));
}
}
-desktop_t *find_desktop(char *name)
+monitor_t *find_monitor(char *name)
{
- desktop_t *d = desk_head;
- while (d != NULL) {
- if (strcmp(d->name, name) == 0)
- return d;
- d = d->next;
- }
+ for (monitor_t *m = mon_head; m != NULL; m = m->next)
+ if (strcmp(m->name, name) == 0)
+ return m;
return NULL;
}
-void add_desktop(char *name)
+void add_desktop(monitor_t *m, char *name)
{
desktop_t *d = make_desktop(name);
- desk_tail->next = d;
- d->prev = desk_tail;
- desk_tail = d;
+ if (m->desk == NULL) {
+ m->desk = d;
+ m->desk_head = d;
+ m->desk_tail = d;
+ } else {
+ m->desk_tail->next = d;
+ d->prev = m->desk_tail;
+ m->desk_tail = d;
+ }
num_desktops++;
ewmh_update_number_of_desktops();
ewmh_update_desktop_names();
}
+
+void add_monitor(xcb_rectangle_t *rect)
+{
+ monitor_t *m = make_monitor(rect);
+ if (mon == NULL) {
+ mon = m;
+ mon_head = m;
+ mon_tail = m;
+ } else {
+ mon_tail->next = m;
+ m->prev = mon_tail;
+ mon_tail = m;
+ }
+ num_monitors++;
+}
void rotate_tree(node_t *, rotate_t);
void magnetise_tree(node_t *, corner_t);
void update_root_dimensions(void);
-void apply_layout(desktop_t *, node_t *, xcb_rectangle_t);
+void arrange(monitor_t *, desktop_t *);
+void apply_layout(monitor_t *, desktop_t *, node_t *, xcb_rectangle_t);
void insert_node(desktop_t *, node_t *);
-void dump_tree(desktop_t *, node_t *, char *, int);
-void list_desktops(char *);
-void focus_node(desktop_t *, node_t *, bool);
+void dump_tree(desktop_t *, node_t *, char *, unsigned int);
+void list_desktops(monitor_t *, list_option_t, unsigned int, char *);
+void list_monitors(list_option_t, char *);
+void focus_node(monitor_t *, desktop_t *, node_t *, bool);
void update_current(void);
void unlink_node(desktop_t *, node_t *);
void remove_node(desktop_t *, node_t *);
void swap_nodes(node_t *, node_t *);
-void transfer_node(desktop_t *, desktop_t *, node_t *);
+void fit_monitor(monitor_t *, client_t *);
+void translate_coordinates(monitor_t *, monitor_t *, client_t *);
+void transfer_node(monitor_t *, desktop_t *, monitor_t *, desktop_t *, node_t *);
+void select_monitor(monitor_t *);
void select_desktop(desktop_t *);
+void cycle_monitor(cycle_dir_t);
void cycle_desktop(cycle_dir_t);
-void cycle_leaf(desktop_t *, node_t *, cycle_dir_t, skip_client_t);
+void cycle_leaf(cycle_dir_t, skip_client_t);
void update_vacant_state(node_t *);
-desktop_t *find_desktop(char *);
-void add_desktop(char *);
+monitor_t *find_monitor(char *);
+void add_desktop(monitor_t *, char *);
+void add_monitor(xcb_rectangle_t *);
#endif
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
+#include "bspwm.h"
#include "settings.h"
#include "types.h"
return n;
}
+monitor_t *make_monitor(xcb_rectangle_t *rect)
+{
+ monitor_t *m = malloc(sizeof(monitor_t));
+ snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
+ m->prev = m->next = NULL;
+ m->desk = m->last_desk = NULL;
+ if (rect != NULL)
+ m->rectangle = *rect;
+ else
+ warn("no rectangle was given for monitor '%s'\n", m->name);
+ return m;
+}
+
desktop_t *make_desktop(const char *name)
{
desktop_t *d = malloc(sizeof(desktop_t));
- strncpy(d->name, name, sizeof(d->name));
+ if (name == NULL)
+ snprintf(d->name, sizeof(d->name), "%s%02d", DEFAULT_DESK_NAME, ++desktop_uid);
+ else
+ strncpy(d->name, name, sizeof(d->name));
d->layout = LAYOUT_TILED;
d->prev = d->next = NULL;
d->root = d->focus = d->last_focus = NULL;
#include "helpers.h"
#define SPLIT_RATIO 0.5
-#define DEFAULT_DESK_NAME "One"
+#define DEFAULT_DESK_NAME "Desktop"
+#define DEFAULT_MON_NAME "Monitor"
#define MISSING_VALUE "N/A"
typedef enum {
CHANGE_DECREASE
} value_change_t;
+typedef enum {
+ LIST_OPTION_VERBOSE,
+ LIST_OPTION_QUIET
+} list_option_t;
+
typedef enum {
SKIP_NONE,
SKIP_FLOATING,
desktop_t *next;
};
+typedef struct monitor_t monitor_t;
+struct monitor_t {
+ char name[MAXLEN];
+ xcb_rectangle_t rectangle;
+ desktop_t *desk;
+ desktop_t *last_desk;
+ desktop_t *desk_head;
+ desktop_t *desk_tail;
+ monitor_t *prev;
+ monitor_t *next;
+};
+
typedef struct {
char name[MAXLEN];
} rule_cause_t;
typedef struct {
node_t *node;
desktop_t *desktop;
+ monitor_t *monitor;
} window_location_t;
+typedef struct {
+ desktop_t *desktop;
+ monitor_t *monitor;
+} desktop_location_t;
+
typedef struct {
xcb_point_t position;
xcb_button_t button;
xcb_rectangle_t rectangle;
+ monitor_t *monitor;
desktop_t *desktop;
node_t *node;
corner_t corner;
} pointer_state_t;
node_t *make_node(void);
+monitor_t *make_monitor(xcb_rectangle_t *);
desktop_t *make_desktop(const char *);
client_t *make_client(xcb_window_t);
rule_t *make_rule(void);
bool locate_window(xcb_window_t win, window_location_t *loc)
{
- node_t *n;
- desktop_t *d = desk_head;
-
- if (d == NULL)
- return false;
+ monitor_t *m = mon_head;
+ while (m != NULL) {
+ desktop_t *d = m->desk_head;
+ while (d != NULL) {
+ node_t *n = first_extrema(d->root);
+ while (n != NULL) {
+ if (n->client->window == win) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = n;
+ return true;
+ }
+ n = next_leaf(n);
+ }
+ d = d->next;
+ }
+ m = m->next;
+ }
+ return false;
+}
- while (d != NULL) {
- n = first_extrema(d->root);
- while (n != NULL) {
- if (n->client->window == win) {
+bool locate_desktop(char *name, desktop_location_t *loc)
+{
+ monitor_t *m = mon_head;
+ while (m != NULL) {
+ desktop_t *d = m->desk_head;
+ while (d != NULL) {
+ if (strcmp(d->name, name) == 0) {
+ loc->monitor = m;
loc->desktop = d;
- loc->node = n;
return true;
}
- n = next_leaf(n);
+ d = d->next;
}
- d = d->next;
+ m = m->next;
}
-
return false;
}
-void window_draw_border(node_t *n, bool focused)
+void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
{
if (n == NULL)
return;
xcb_gcontext_t gc = xcb_generate_id(dpy);
xcb_create_gc(dpy, gc, pix, 0, NULL);
- uint32_t main_border_color_pxl = get_main_border_color(n->client, focused);
+ uint32_t main_border_color_pxl = get_main_border_color(n->client, focused_window, focused_monitor);
/* inner border */
if (inner_border_width > 0) {
xcb_poly_fill_rectangle(dpy, pix, gc, LENGTH(outer_rectangles), outer_rectangles);
}
- if (split_mode == MODE_MANUAL && focused) {
+ if (split_mode == MODE_MANUAL && focused_monitor && focused_window) {
uint16_t fence = (int16_t) (n->split_ratio * ((split_dir == DIR_UP || split_dir == DIR_DOWN) ? height : width));
presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
switch (split_dir) {
{
char line[MAXLEN];
- desktop_t *d = desk_head;
-
- while (d != NULL) {
- node_t *n = first_extrema(d->root);
- while (n != NULL) {
- snprintf(line, sizeof(line), "0x%X\n", n->client->window);
- strncat(rsp, line, REMLEN(rsp));
- n = next_leaf(n);
- }
- d = d->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)) {
+ snprintf(line, sizeof(line), "0x%X\n", n->client->window);
+ strncat(rsp, line, REMLEN(rsp));
+ }
}
-uint32_t get_main_border_color(client_t *c, bool focused)
+uint32_t get_main_border_color(client_t *c, bool focused_window, bool focused_monitor)
{
if (c == NULL)
return 0;
- if (focused) {
+ if (focused_monitor && focused_window) {
+ if (c->locked)
+ return focused_locked_border_color_pxl;
+ else
+ return focused_border_color_pxl;
+ } else if (focused_window) {
if (c->locked)
return active_locked_border_color_pxl;
else
c->floating_rectangle = (xcb_rectangle_t) {geom->x, geom->y, geom->width, geom->height};
free(geom);
} else {
- c->floating_rectangle = (xcb_rectangle_t) {0, 0, 1, 1};
+ c->floating_rectangle = (xcb_rectangle_t) {0, 0, 32, 24};
}
}
#include "types.h"
bool locate_window(xcb_window_t, window_location_t *);
-void window_draw_border(node_t *, bool);
+bool locate_desktop(char *, desktop_location_t *);
+void window_draw_border(node_t *, bool, bool);
void window_close(node_t *);
void window_kill(desktop_t *, node_t *);
void toggle_fullscreen(client_t *);
void window_set_visibility(xcb_window_t, bool);
void window_hide(xcb_window_t);
void window_show(xcb_window_t);
-uint32_t get_main_border_color(client_t *, bool);
+uint32_t get_main_border_color(client_t *, bool, bool);
void update_floating_rectangle(client_t *);
void list_windows(char *);