status_fifo = NULL;
last_motion_time = last_motion_x = last_motion_y = 0;
randr_base = 0;
- visible = auto_raise = true;
+ visible = auto_raise = sticky_still = true;
exit_status = 0;
}
bool visible;
bool auto_raise;
+bool sticky_still;
bool running;
bool randr;
_bspc() {
local commands='window desktop monitor query pointer rule restore control config 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 top_padding right_padding bottom_padding left_padding split_ratio growth_factor borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus honor_ewmh_focus'
+ 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 focused_sticky_border_color normal_sticky_border_color urgent_border_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio growth_factor borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus honor_ewmh_focus'
COMPREPLY=()
_bspc() {
local -a commands settings
commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'rule' 'restore' 'control' 'config' 'quit')
- 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' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'honor_ewmh_focus')
+ 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' 'focused_sticky_border_color' 'normal_sticky_border_color' 'urgent_border_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'honor_ewmh_focus')
if (( CURRENT == 2 )) ; then
_values 'command' "$commands[@]"
elif (( CURRENT == 3 )) ; then
d->root = d->focus = NULL;
d->window_gap = WINDOW_GAP;
d->border_width = BORDER_WIDTH;
+ d->num_sticky = 0;
return d;
}
Rotate the tree holding the edge located in the given direction in relation to the selected window\&.
.RE
.PP
-\fB\-t\fR, \fB\-\-toggle\fR floating|fullscreen|locked[=on|off]
+\fB\-t\fR, \fB\-\-toggle\fR floating|fullscreen|locked|sticky[=on|off]
.RS 4
Set or toggle the given state for the selected window\&.
.RE
\fBOptions\fR
.RS 4
.PP
-\fB\-a\fR, \fB\-\-add\fR <pattern> [\-d \fIDESKTOP_SEL\fR [\-\-follow]] [\-\-floating] [\-\-fullscreen] [\-\-locked] [\-\-focus] [\-\-unmanage] [\-\-one\-shot]
+\fB\-a\fR, \fB\-\-add\fR <pattern> [\-d \fIDESKTOP_SEL\fR [\-\-follow]] [\-\-floating] [\-\-fullscreen] [\-\-locked] [\-\-sticky] [\-\-focus] [\-\-unmanage] [\-\-one\-shot]
.RS 4
Create a new rule (<pattern> must match the class or instance name)\&.
.RE
Color of the border of an unfocused locked window\&.
.RE
.PP
+\fIfocused_sticky_border_color\fR
+.RS 4
+Color of the border of a focused sticky window of a focused monitor\&.
+.RE
+.PP
+\fInormal_sticky_border_color\fR
+.RS 4
+Color of the border of an unfocused sticky window\&.
+.RE
+.PP
\fIurgent_border_color\fR
.RS 4
Color of the border of an urgent window\&.
*-R*, *--rotate* 'DIR' '90|270|180'::
Rotate the tree holding the edge located in the given direction in relation to the selected window.
-*-t*, *--toggle* floating|fullscreen|locked[=on|off]::
+*-t*, *--toggle* floating|fullscreen|locked|sticky[=on|off]::
Set or toggle the given state for the selected window.
*-c*, *--close*::
Options
^^^^^^^
-*-a*, *--add* <pattern> [-d 'DESKTOP_SEL' [--follow]] [--floating] [--fullscreen] [--locked] [--focus] [--unmanage] [--one-shot]::
+*-a*, *--add* <pattern> [-d 'DESKTOP_SEL' [--follow]] [--floating] [--fullscreen] [--locked] [--sticky] [--focus] [--unmanage] [--one-shot]::
Create a new rule (<pattern> must match the class or instance name).
*-r*, *--remove* <rule_uid>|tail|head...::
'normal_locked_border_color'::
Color of the border of an unfocused locked window.
+'focused_sticky_border_color'::
+ Color of the border of a focused sticky window of a focused monitor.
+
+'normal_sticky_border_color'::
+ Color of the border of an unfocused sticky window.
+
'urgent_border_color'::
Color of the border of an urgent window.
dirty = true;
} else if (streq("locked", key)) {
set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
+ } else if (streq("sticky", key)) {
+ set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->sticky));
}
} else if (streq("-p", *args) || streq("--presel", *args)) {
num--, args++;
rule->effect.fullscreen = true;
} else if (streq("--locked", *args)) {
rule->effect.locked = true;
+ } else if (streq("--sticky", *args)) {
+ rule->effect.sticky = true;
} else if (streq("--follow", *args)) {
rule->effect.follow = true;
} else if (streq("--focus", *args)) {
SETCOLOR(focused_locked_border_color)
SETCOLOR(active_locked_border_color)
SETCOLOR(normal_locked_border_color)
+ SETCOLOR(focused_sticky_border_color)
+ SETCOLOR(normal_sticky_border_color)
SETCOLOR(urgent_border_color)
#undef SETCOLOR
} else if (streq("focus_follows_pointer", name)) {
GETCOLOR(focused_locked_border_color)
GETCOLOR(active_locked_border_color)
GETCOLOR(normal_locked_border_color)
+ GETCOLOR(focused_sticky_border_color)
+ GETCOLOR(normal_sticky_border_color)
GETCOLOR(urgent_border_color)
#undef GETCOLOR
#define GETBOOL(s) \
if (is_leaf(n)) {
client_t *c = n->client;
- snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (n->split_mode ? 'p' : '-'));
+ snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (n->split_mode ? 'p' : '-'));
} else {
snprintf(line, sizeof(line), "%c %c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
}
} else {
client_t *c = make_client(XCB_NONE);
num_clients++;
- char floating, transient, fullscreen, urgent, locked, sd, sm, end = 0;
- sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &transient, &fullscreen, &urgent, &locked, &sm, &end);
+ char floating, transient, fullscreen, urgent, locked, sticky, sd, sm, end = 0;
+ sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &transient, &fullscreen, &urgent, &locked, &sticky, &sm, &end);
c->floating = (floating == '-' ? false : true);
c->transient = (transient == '-' ? false : true);
c->fullscreen = (fullscreen == '-' ? false : true);
c->urgent = (urgent == '-' ? false : true);
c->locked = (locked == '-' ? false : true);
+ c->sticky = (sticky == '-' ? false : true);
n->split_mode = (sm == '-' ? MODE_AUTOMATIC : MODE_MANUAL);
if (sd == 'U')
n->split_dir = DIR_UP;
n->client = c;
if (end != 0)
d->focus = n;
+ if (c->sticky)
+ d->num_sticky++;
}
if (br == 'a')
n->birth_rotation = 90;
r->effect.floating = false;
r->effect.fullscreen = false;
r->effect.locked = false;
+ r->effect.sticky = false;
r->effect.follow = false;
r->effect.focus = false;
r->effect.unmanage = false;
return false;
}
-void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, bool *floating, bool *fullscreen, bool *locked, bool *follow, bool *transient, bool *takes_focus, bool *manage)
+void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, bool *floating, bool *fullscreen, bool *locked, bool *sticky, bool *follow, bool *transient, bool *takes_focus, bool *manage)
{
xcb_ewmh_get_atoms_reply_t win_type;
*fullscreen = true;
if (efc.locked)
*locked = true;
+ if (efc.sticky)
+ *sticky = true;
if (efc.follow)
*follow = true;
if (efc.focus)
remove_rule(rule);
rule = next;
}
+
+ if (*sticky) {
+ *m = mon;
+ *d = mon->desk;
+ }
}
void list_rules(char *pattern, char *rsp)
strncat(rsp, " --fullscreen", REMLEN(rsp));
if (r->effect.locked)
strncat(rsp, " --locked", REMLEN(rsp));
+ if (r->effect.sticky)
+ strncat(rsp, " --sticky", REMLEN(rsp));
if (r->effect.follow)
strncat(rsp, " --follow", REMLEN(rsp));
if (r->effect.focus)
void prune_rules(desktop_t *);
rule_t *find_rule(unsigned int);
bool is_match(rule_t *, xcb_window_t);
-void handle_rules(xcb_window_t, monitor_t **, desktop_t **, bool *, bool *, bool *, bool *, bool *, bool *, bool *);
+void handle_rules(xcb_window_t, monitor_t **, desktop_t **, bool *, bool *, bool *, bool *, bool *, bool *, bool *, bool *);
void list_rules(char *, char *);
#endif
snprintf(focused_locked_border_color, sizeof(focused_locked_border_color), "%s", FOCUSED_LOCKED_BORDER_COLOR);
snprintf(active_locked_border_color, sizeof(active_locked_border_color), "%s", ACTIVE_LOCKED_BORDER_COLOR);
snprintf(normal_locked_border_color, sizeof(normal_locked_border_color), "%s", NORMAL_LOCKED_BORDER_COLOR);
+ snprintf(focused_sticky_border_color, sizeof(focused_sticky_border_color), "%s", FOCUSED_STICKY_BORDER_COLOR);
+ snprintf(normal_sticky_border_color, sizeof(normal_sticky_border_color), "%s", NORMAL_STICKY_BORDER_COLOR);
snprintf(urgent_border_color, sizeof(urgent_border_color), "%s", URGENT_BORDER_COLOR);
split_ratio = SPLIT_RATIO;
#define FOCUSED_LOCKED_BORDER_COLOR "#C7B579"
#define ACTIVE_LOCKED_BORDER_COLOR "#545350"
#define NORMAL_LOCKED_BORDER_COLOR "#3F3E3B"
+#define FOCUSED_STICKY_BORDER_COLOR "#E3A5DA"
+#define NORMAL_STICKY_BORDER_COLOR "#3F3E3B"
#define URGENT_BORDER_COLOR "#EFA29A"
#define SPLIT_RATIO 0.5
char focused_locked_border_color[MAXLEN];
char active_locked_border_color[MAXLEN];
char normal_locked_border_color[MAXLEN];
+char focused_sticky_border_color[MAXLEN];
+char normal_sticky_border_color[MAXLEN];
char urgent_border_color[MAXLEN];
double split_ratio;
if (f->vacant)
update_vacant_state(p);
}
+ if (n->client->sticky)
+ d->num_sticky++;
put_status();
}
if (mon->desk != d || n == NULL)
clear_input_focus();
+ if (mon->desk->num_sticky > 0 && d != mon->desk) {
+ node_t *a = first_extrema(mon->desk->root);
+ sticky_still = false;
+ while (a != NULL) {
+ node_t *b = next_leaf(a, mon->desk->root);
+ if (a->client->sticky)
+ transfer_node(mon, mon->desk, a, m, d, d->focus);
+ a = b;
+ }
+ sticky_still = true;
+ if (n == NULL)
+ n = d->focus;
+ }
+
if (n != NULL && d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
set_fullscreen(d->focus, false);
arrange(m, d);
snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
c->border_width = BORDER_WIDTH;
c->window = win;
- c->floating = c->transient = c->fullscreen = c->locked = c->urgent = false;
+ c->floating = c->transient = c->fullscreen = c->locked = c->sticky = c->urgent = false;
c->icccm_focus = false;
xcb_icccm_get_wm_protocols_reply_t protocols;
if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
update_vacant_state(b->parent);
}
+ if (n->client->sticky)
+ d->num_sticky--;
put_status();
}
void swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
{
- if (n1 == NULL || n2 == NULL || n1 == n2)
+ if (n1 == NULL || n2 == NULL || n1 == n2 || (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
return;
PRINTF("swap nodes %X %X", n1->client->window, n2->client->window);
void transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
{
- if (ns == NULL || ns == nd)
+ if (ns == NULL || ns == nd || (sticky_still && ns->client->sticky))
return;
PRINTF("transfer node %X\n", ns->client->window);
if (ds != dd) {
ewmh_set_wm_desktop(ns, dd);
- if (ds == ms->desk && dd != md->desk)
- window_hide(ns->client->window);
- else if (ds != ms->desk && dd == md->desk)
- window_show(ns->client->window);
+ if (!ns->client->sticky) {
+ if (ds == ms->desk && dd != md->desk)
+ window_hide(ns->client->window);
+ else if (ds != ms->desk && dd == md->desk)
+ window_show(ns->client->window);
+ }
}
history_transfer_node(md, dd, ns);
bool transient; /* transient window are always floating */
bool fullscreen;
bool locked; /* protects window from being closed */
+ bool sticky;
bool urgent;
bool icccm_focus;
xcb_rectangle_t floating_rectangle;
desktop_t *next;
int window_gap;
unsigned int border_width;
+ int num_sticky;
};
typedef struct monitor_t monitor_t;
bool floating;
bool fullscreen;
bool locked;
+ bool sticky;
bool follow;
bool focus;
bool unmanage;
if (override_redirect || locate_window(win, &loc))
return;
- bool floating = false, fullscreen = false, locked = false, follow = false, transient = false, takes_focus = true, manage = true;
- handle_rules(win, &m, &d, &floating, &fullscreen, &locked, &follow, &transient, &takes_focus, &manage);
+ bool floating = false, fullscreen = false, locked = false, sticky = false, follow = false, transient = false, takes_focus = true, manage = true;
+ handle_rules(win, &m, &d, &floating, &fullscreen, &locked, &sticky, &follow, &transient, &takes_focus, &manage);
if (!manage) {
disable_floating_atom(win);
disable_floating_atom(c->window);
set_floating(n, floating);
set_locked(m, d, n, locked);
+ set_sticky(m, d, n, sticky);
if (d->focus != NULL && d->focus->client->fullscreen)
set_fullscreen(d->focus, false);
window_draw_border(n, d->focus == n, m == mon);
}
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL || n->client->sticky == value)
+ return;
+
+ client_t *c = n->client;
+
+ PRINTF("set sticky %X: %s\n", c->window, BOOLSTR(value));
+
+ c->sticky = value;
+ if (value)
+ d->num_sticky++;
+ else
+ d->num_sticky--;
+ window_draw_border(n, d->focus == n, m == mon);
+}
+
void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
{
if (value && mon->desk->focus == n)
if (focused_monitor && focused_window) {
if (c->locked)
get_color(focused_locked_border_color, c->window, &pxl);
+ else if (c->sticky)
+ get_color(focused_sticky_border_color, c->window, &pxl);
else
get_color(focused_border_color, c->window, &pxl);
} else if (focused_window) {
get_color(urgent_border_color, c->window, &pxl);
else if (c->locked)
get_color(normal_locked_border_color, c->window, &pxl);
+ else if (c->sticky)
+ get_color(normal_sticky_border_color, c->window, &pxl);
else
get_color(normal_border_color, c->window, &pxl);
}
void set_fullscreen(node_t *, bool);
void set_floating(node_t *, bool);
void set_locked(monitor_t *, desktop_t *, node_t *, bool);
+void set_sticky(monitor_t *, desktop_t *, node_t *, bool);
void set_urgency(monitor_t *, desktop_t *, node_t *, bool);
void set_floating_atom(xcb_window_t, uint32_t);
void enable_floating_atom(xcb_window_t);