bspc.o: bspc.c helpers.h common.h
bspwm.o: bspwm.c types.h helpers.h desktop.h monitor.h settings.h \
- messages.h events.h common.h tree.h window.h history.h stack.h rule.h \
- ewmh.h bspwm.h
+ messages.h events.h common.h tree.h window.h history.h stack.h ewmh.h \
+ bspwm.h
desktop.o: desktop.c bspwm.h types.h helpers.h ewmh.h history.h monitor.h \
query.h tree.h window.h desktop.h
events.o: events.c bspwm.h types.h helpers.h ewmh.h monitor.h query.h \
settings.h tree.h window.h events.h
ewmh.o: ewmh.c bspwm.h types.h helpers.h settings.h tree.h ewmh.h
helpers.o: helpers.c bspwm.h types.h helpers.h
-history.o: history.c bspwm.h types.h helpers.h tree.h query.h
+history.o: history.c bspwm.h types.h helpers.h query.h
messages.o: messages.c bspwm.h types.h helpers.h desktop.h ewmh.h \
- history.h monitor.h pointer.h query.h restore.h rule.h settings.h tree.h \
+ history.h monitor.h pointer.h query.h restore.h settings.h tree.h \
window.h messages.h
monitor.o: monitor.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
query.h settings.h tree.h window.h monitor.h
query.o: query.c bspwm.h types.h helpers.h desktop.h history.h messages.h \
monitor.h tree.h query.h
restore.o: restore.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
- monitor.h window.h query.h settings.h stack.h tree.h restore.h
+ monitor.h window.h query.h stack.h tree.h restore.h
rule.o: rule.c bspwm.h types.h helpers.h ewmh.h window.h messages.h \
- query.h rule.h
+ settings.h query.h rule.h
settings.o: settings.c bspwm.h types.h helpers.h settings.h
stack.o: stack.c bspwm.h types.h helpers.h window.h stack.h
tree.o: tree.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
#include "window.h"
#include "history.h"
#include "stack.h"
-#include "rule.h"
#include "ewmh.h"
#include "bspwm.h"
num_monitors = num_desktops = num_clients = 0;
monitor_uid = desktop_uid = 0;
mon = mon_head = mon_tail = pri_mon = NULL;
- rule_head = rule_tail = NULL;
history_head = history_tail = history_needle = NULL;
stack_head = stack_tail = NULL;
status_fifo = NULL;
{
while (mon_head != NULL)
remove_monitor(mon_head);
- while (rule_head != NULL)
- remove_rule(rule_head);
while (stack_head != NULL)
remove_stack(stack_head);
empty_history();
history_t *history_needle;
stacking_list_t *stack_head;
stacking_list_t *stack_tail;
-rule_t *rule_head;
-rule_t *rule_tail;
pointer_state_t *frozen_pointer;
xcb_window_t motion_recorder;
_bspc() {
- local commands='window desktop monitor query pointer rule restore control config quit'
+ local commands='window desktop monitor query pointer 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 focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color focused_frame_opacity active_frame_opacity normal_frame_opacity 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 ignore_ewmh_focus'
+ local settings='rule_command 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 focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color focused_frame_opacity active_frame_opacity normal_frame_opacity 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 ignore_ewmh_focus'
COMPREPLY=()
--- /dev/null
+Synopsis
+--------
+
+Server
+~~~~~~
+
+*ruld* [*-h*|*-p* 'PORT']
+
+Client
+~~~~~~
+
+*rulc* [*-h*|*-p* 'PORT'|*-a*|*-r*|*-l*|*-t*|*-q*] 'DATA' ...
+
+Options
+-------
+
+Shared
+~~~~~~
+
+*-h*::
+ Print the synopsis and exit.
+
+*-p* 'PORT'::
+ Set the socket port.
+
+Client
+~~~~~~
+
+*-a* 'HYPOT' 'CONSEQ' ['[duration=DURATION][,delay=DELAY]']::
+ Create a new rule. 'HYPOT' is a Lua expression that represent the rule hypothesis and involves the following strings: 'class', 'instance', 'title', 'type' and 'state'.
+
+*-r* 'STRING'|head|tail::
+ Remove the rules containing 'STRING' in their hypothesis or remove the first or last rule.
+
+*-l*::
+ List the rules.
+
+*-t* 'CLASS' 'INSTANCE' 'TITLE' 'TYPE' 'STATE'::
+ Test the rules for the given window informations.
+
+*-q*::
+ Kill the server.
--- /dev/null
+#! /usr/bin/env lua
+
+local p = require "posix"
+local port = 54321
+
+local short = "hp:arltq"
+local long = {
+ {"help", "none", 'h'},
+ {"port", "required", 'p'},
+ {"add", "none", 'a'},
+ {"remove", "none", 'r'},
+ {"list", "none", 'l'},
+ {"test", "none", 't'},
+ {"quit", "none", 'q'}
+}
+
+local cmd_assoc = {
+ a = "add",
+ r = "remove",
+ l = "list",
+ t = "test",
+ q = "quit"
+}
+
+local cmd
+local data_idx = 1
+for opt, optarg, optind, longind in p.getopt(arg, short, long) do
+ if opt == '?' then
+ print("Unrecognized option")
+ os.exit(1)
+ elseif opt == 'h' then
+ print("Usage: rulc [-h|-p PORT|-a|-r|-l|-t|-q] DATA ...")
+ os.exit(0)
+ elseif opt == 'p' then
+ port = optarg
+ else
+ cmd = cmd_assoc[opt]
+ end
+ data_idx = optind
+end
+
+local msg = cmd
+if cmd == "test" then
+ msg = string.format("%s {class=%q, instance=%q, title=%q, type=%q, state=%q}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2], arg[data_idx+3], arg[data_idx+4])
+elseif cmd == "add" then
+ msg = string.format("%s {%q, %q, %s}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2] and string.format("{%s}", arg[data_idx+2]) or "")
+elseif cmd == "remove" then
+ msg = string.format("%s %s", msg, arg[data_idx])
+end
+
+local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
+local s = p.connect(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
+if not s then
+ p.close(fd)
+ os.exit(1)
+end
+p.send(fd, msg)
+rsp = p.recv(fd, p.BUFSIZ)
+
+if rsp and #rsp > 0 then
+ print(rsp)
+end
+
+p.close(fd)
--- /dev/null
+#! /usr/bin/env lua
+
+local p = require("posix")
+local port = 54321
+
+local short = "hp:"
+local long = {
+ {"help", "none", 'h'},
+ {"port", "required", 'p'},
+}
+
+local rules = {
+ {'type:find("toolbar") or type:find("utility")', 'focus=off'},
+ {'type:find("dialog")', 'floating=on'},
+ {'type:find("dock") or type:find("desktop") or type:find("notification")', 'manage=off'},
+ {'type:find("desktop")', 'lower=on'},
+ {'state:find("fullscreen")', 'fullscreen=on'},
+ {'state:find("sticky")', 'sticky=on'}
+}
+
+for opt, optarg, optind, longind in p.getopt(arg, short, long) do
+ if opt == '?' then
+ print("Unrecognized option")
+ os.exit(1)
+ elseif opt == 'h' then
+ print("Usage: ruld [-h|-p PORT]")
+ os.exit(0)
+ elseif opt == 'p' then
+ port = optarg
+ end
+end
+
+function eval(exp, env)
+ local f
+ local value = "return " .. exp
+ if env then
+ f = load(value, nil, nil, env)
+ else
+ f = load(value)
+ end
+ return f and f()
+end
+
+function test(env)
+ local rsp = ""
+ for index = #rules, 1, -1 do
+ local entry = rules[index]
+ if eval(entry[1], env) then
+ local delay, duration
+ if entry[3] then
+ delay = entry[3].delay
+ duration = entry[3].duration
+ if delay and delay > 0 then
+ entry[3].delay = delay - 1
+ end
+ if ((not delay) or delay == 0) and duration and duration > 0 then
+ entry[3].duration = duration - 1
+ end
+ end
+ if (((not delay) or delay == 0) and ((not duration) or duration > 0)) then
+ if #rsp > 0 then
+ rsp = rsp .. " " .. entry[2]
+ else
+ rsp = entry[2]
+ end
+ end
+ if duration and duration <= 1 then
+ table.remove(rules, index)
+ end
+ end
+ end
+ return rsp
+end
+
+local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
+p.bind(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
+p.listen(fd, p.SOMAXCONN)
+local running = true
+
+while running do
+ ret_fd = p.accept(fd)
+ if ret_fd then
+ local msg = p.recv(ret_fd, p.BUFSIZ)
+ if msg then
+ local cmd, data = nil
+ sep = msg:find(" ")
+ if sep then
+ cmd = msg:sub(1, sep - 1)
+ data = msg:sub(sep + 1)
+ else
+ cmd = msg
+ end
+ if cmd == "test" then
+ local env = eval(data)
+ if env then
+ rsp = test(env)
+ p.send(ret_fd, rsp)
+ end
+ elseif cmd == "add" then
+ local value = eval(data)
+ if value then
+ table.insert(rules, value)
+ end
+ elseif cmd == "remove" then
+ if data == "tail" then
+ table.remove(rules, #rules)
+ elseif data == "head" then
+ table.remove(rules, 1)
+ else
+ for index = #rules, 1, -1 do
+ if rules[index][1]:find(data) then
+ table.remove(rules, index)
+ end
+ end
+ end
+ elseif cmd == "quit" then
+ running = false
+ elseif cmd == "list" then
+ local rsp = ""
+ for index, entry in pairs(rules) do
+ rsp = rsp .. string.format("%s => %s", entry[1], entry[2])
+ if entry[3] then
+ if entry[3].delay then
+ rsp = rsp .. string.format(" @%i", entry[3].delay)
+ end
+ if entry[3].duration then
+ rsp = rsp .. string.format(" +%i", entry[3].duration)
+ end
+ end
+ if index < #rules then
+ rsp = rsp .. "\n"
+ end
+ end
+ p.send(ret_fd, rsp)
+ end
+ end
+ p.close(ret_fd)
+ end
+end
+
+p.close(fd)
_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' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' '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' 'ignore_ewmh_focus')
+ commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'restore' 'control' 'config' 'quit')
+ settings=('rule_command' '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' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' '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' 'ignore_ewmh_focus')
if (( CURRENT == 2 )) ; then
_values 'command' "$commands[@]"
elif (( CURRENT == 3 )) ; then
.\" Title: bspwm
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 11/03/2013
+.\" Date: 11/05/2013
.\" Manual: Bspwm Manual
.\" Source: Bspwm 0.8.6
.\" Language: English
.\"
-.TH "BSPWM" "1" "11/03/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
+.TH "BSPWM" "1" "11/05/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
Terminate the current pointer action\&.
.RE
.RE
-.SS "Rule"
-.sp
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBGeneral Syntax\fR
-.RS 4
-.sp
-rule \fIOPTIONS\fR
-.RE
-.sp
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBOptions\fR
-.RS 4
-.PP
-\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name> [\-d \fIDESKTOP_SEL\fR [\-\-follow]] [\-\-floating] [\-\-fullscreen] [\-\-locked] [\-\-sticky] [\-\-focus] [\-\-frame] [\-\-private] [\-\-center] [\-\-unmanage] [\-\-one\-shot]
-.RS 4
-Create a new rule\&.
-.RE
-.PP
-\fB\-r\fR, \fB\-\-remove\fR <name>|^<n>|tail|head\&...
-.RS 4
-Remove the rules with the given names or indexes\&.
-.RE
-.PP
-\fB\-l\fR, \fB\-\-list\fR [<name>]
-.RS 4
-List the rules\&.
-.RE
-.RE
.SS "Config"
.sp
.it 1 an-trap
All the boolean settings are \fIfalse\fR by default\&.
.SS "Global Settings"
.PP
+\fIrule_command\fR
+.RS 4
+External command used to retrieve rule consequences\&. It must contain at least one integer format directive which will be replaced by the ID of the window being processed\&. The output of that command must have the following format:
+\fBkey1=value1 key2=value2 \&...\fR, where
+\fBkeyN\fR
+is one of
+\fIfloating\fR,
+\fIfullscreen\fR,
+\fIlocked\fR,
+\fIsticky\fR,
+\fIprivate\fR,
+\fIframe\fR,
+\fIcenter\fR,
+\fIlower\fR,
+\fIfollow\fR,
+\fImanage\fR,
+\fIfocus\fR,
+\fIdesktop\fR
+or
+\fImonitor\fR\&.
+.RE
+.PP
\fIfocused_border_color\fR
.RS 4
Color of the border of a focused window of a focused monitor\&.
*-u*, *--ungrab*::
Terminate the current pointer action.
-Rule
-~~~~
-
-General Syntax
-^^^^^^^^^^^^^^
-
-rule 'OPTIONS'
-
-Options
-^^^^^^^
-
-*-a*, *--add* <class_name>|<instance_name> [-d 'DESKTOP_SEL' [--follow]] [--floating] [--fullscreen] [--locked] [--sticky] [--focus] [--frame] [--private] [--center] [--unmanage] [--one-shot]::
- Create a new rule.
-
-*-r*, *--remove* <name>|^<n>|tail|head...::
- Remove the rules with the given names or indexes.
-
-*-l*, *--list* [<name>]::
- List the rules.
-
Config
~~~~~~
Global Settings
~~~~~~~~~~~~~~~
+'rule_command'::
+ External command used to retrieve rule consequences. It must contain at least one integer format directive which will be replaced by the ID of the window being processed. The output of that command must have the following format: *key1=value1 key2=value2 ...*, where *keyN* is one of 'floating', 'fullscreen', 'locked', 'sticky', 'private', 'frame', 'center', 'lower', 'follow', 'manage', 'focus', 'desktop' or 'monitor'.
+
'focused_border_color'::
Color of the border of a focused window of a focused monitor.
bspc monitor -d I II III IV V VI VII VIII IX X
-bspc rule -a Gimp -d ^8 --follow --floating
-bspc rule -a Chromium -d ^2
-bspc rule -a mplayer2 --floating
-bspc rule -a Kupfer.py --focus
-bspc rule -a Screenkey --unmanage
-
bspc config border_width 2
bspc config window_gap 12
bspc config split_ratio 0.52
bspc config borderless_monocle true
bspc config gapless_monocle true
+
+bspc config rule_command "xwinfo -cints 0x%X | xargs -d '\n' rulc -t"
+
+rulc -a 'class=="Gimp"' 'desktop=^8 follow=on floating=on'
+rulc -a 'class=="Chromium"' 'desktop=^2'
+rulc -a 'instance=="mpv"' 'floating=on'
+rulc -a 'class=="Kupfer.py"' 'focus=on'
+rulc -a 'class=="Screenkey"' 'manage=off'
#include <stdlib.h>
#include "bspwm.h"
-#include "tree.h"
#include "query.h"
history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
#include "pointer.h"
#include "query.h"
#include "restore.h"
-#include "rule.h"
#include "settings.h"
#include "tree.h"
#include "window.h"
return cmd_restore(++args, --num);
} else if (streq("control", *args)) {
return cmd_control(++args, --num);
- } else if (streq("rule", *args)) {
- return cmd_rule(++args, --num, rsp);
} else if (streq("pointer", *args)) {
return cmd_pointer(++args, --num);
} else if (streq("config", *args)) {
return true;
}
-bool cmd_rule(char **args, int num, char *rsp)
-{
- if (num < 1)
- return false;
- while (num > 0) {
- if (streq("-a", *args) || streq("--add", *args)) {
- num--, args++;
- if (num < 2)
- return false;
- rule_t *rule = make_rule();
- snprintf(rule->cause.name, sizeof(rule->cause.name), "%s", *args);
- num--, args++;
- while (num > 0) {
- if (streq("--floating", *args)) {
- rule->effect.floating = true;
- } else if (streq("--fullscreen", *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)) {
- rule->effect.focus = true;
- } else if (streq("--frame", *args)) {
- rule->effect.frame = true;
- } else if (streq("--private", *args)) {
- rule->effect.private = true;
- } else if (streq("--center", *args)) {
- rule->effect.center = true;
- } else if (streq("--unmanage", *args)) {
- rule->effect.unmanage = true;
- } else if (streq("--one-shot", *args)) {
- rule->one_shot = true;
- } else if (streq("-d", *args) || streq("--desktop", *args)) {
- num--, args++;
- if (num < 1) {
- free(rule);
- return false;
- }
- snprintf(rule->effect.desc, sizeof(rule->effect.desc), "%s", *args);
- } else {
- free(rule);
- return false;
- }
- num--, args++;
- }
- add_rule(rule);
- } else if (streq("-r", *args) || streq("--remove", *args)) {
- num--, args++;
- if (num < 1)
- return false;
- int idx;
- while (num > 0) {
- if (parse_index(*args, &idx))
- remove_rule_by_index(idx - 1);
- else if (streq("tail", *args))
- remove_rule(rule_tail);
- else if (streq("head", *args))
- remove_rule(rule_head);
- else
- remove_rule_by_name(*args);
- num--, args++;
- }
- } else if (streq("-l", *args) || streq("--list", *args)) {
- num--, args++;
- list_rules(num > 0 ? *args : NULL, rsp);
- } else {
- return false;
- }
- num--, args++;
- }
-
- return true;
-}
-
bool cmd_pointer(char **args, int num)
{
if (num < 1)
MONSET(bottom_padding)
MONSET(left_padding)
#undef MONSET
+ } else if (streq("rule_command", name)) {
+ return snprintf(rule_command, sizeof(rule_command), "%s", value) >= 0;
} else if (streq("split_ratio", name)) {
double r;
if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
return false;
else
snprintf(rsp, BUFSIZ, "%u", loc.desktop->border_width);
+ else if (streq("rule_command", name))
+ snprintf(rsp, BUFSIZ, "%s", rule_command);
#define MONGET(k) \
else if (streq(#k, name)) \
if (loc.monitor == NULL) \
bool cmd_desktop(char **args, int num);
bool cmd_monitor(char **args, int num);
bool cmd_query(char **args, int num, char *rsp);
-bool cmd_rule(char **args, int num, char *rsp);
bool cmd_pointer(char **args, int num);
bool cmd_restore(char **args, int num);
bool cmd_control(char **args, int num);
#include "monitor.h"
#include "window.h"
#include "query.h"
-#include "settings.h"
#include "stack.h"
#include "tree.h"
#include "restore.h"
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include "bspwm.h"
#include "ewmh.h"
#include "window.h"
#include "messages.h"
+#include "settings.h"
#include "query.h"
#include "rule.h"
-rule_t *make_rule(void)
+rule_consequence_t *make_rule_conquence(void)
{
- rule_t *r = malloc(sizeof(rule_t));
- 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.frame = false;
- r->effect.private = false;
- r->effect.center = false;
- r->effect.unmanage = false;
- r->one_shot = false;
- r->effect.desc[0] = '\0';
- r->prev = NULL;
- r->next = NULL;
+ rule_consequence_t *r = calloc(1, sizeof(rule_consequence_t));
+ r->manage = r->focus = true;
return r;
}
-void add_rule(rule_t *r)
+void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, rule_consequence_t *csq)
{
- if (rule_head == NULL) {
- rule_head = rule_tail = r;
- } else {
- rule_tail->next = r;
- r->prev = rule_tail;
- rule_tail = r;
- }
-}
-
-void remove_rule(rule_t *r)
-{
- if (r == NULL)
- return;
- rule_t *prev = r->prev;
- rule_t *next = r->next;
- if (prev != NULL)
- prev->next = next;
- if (next != NULL)
- next->prev = prev;
- if (r == rule_head)
- rule_head = next;
- if (r == rule_tail)
- rule_tail = prev;
- free(r);
-}
-
-void remove_rule_by_name(char *name)
-{
- rule_t *r = rule_head;
- while (r != NULL) {
- rule_t *next = r->next;
- if (streq(r->cause.name, name))
- remove_rule(r);
- r = next;
- }
-}
-
-bool remove_rule_by_index(int idx)
-{
- for (rule_t *r = rule_head; r != NULL; r = r->next, idx--)
- if (idx == 0) {
- remove_rule(r);
- return true;
- }
- return false;
-}
-
-bool is_match(rule_t *r, xcb_window_t win)
-{
- xcb_icccm_get_wm_class_reply_t reply;
- int8_t success = 0;
- if (streq(r->cause.name, MATCH_ALL) ||
- ((success = xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL)) == 1
- && (streq(reply.class_name, r->cause.name)
- || streq(reply.instance_name, r->cause.name)))) {
- if (success == 1)
- xcb_icccm_get_wm_class_reply_wipe(&reply);
- return true;
- }
- return false;
-}
-
-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 *frame, bool *private, bool *center, bool *manage)
-{
- xcb_ewmh_get_atoms_reply_t win_type;
-
- if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
- for (unsigned int i = 0; i < win_type.atoms_len; i++) {
- xcb_atom_t a = win_type.atoms[i];
- if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR
- || a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
- *takes_focus = false;
- } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
- *floating = true;
- } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK || a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP || a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
- *manage = false;
- if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP)
- window_lower(win);
- }
- }
- xcb_ewmh_get_atoms_reply_wipe(&win_type);
- }
-
xcb_size_hints_t size_hints;
-
if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
if (size_hints.min_width > 0 && size_hints.min_height > 0
&& size_hints.min_width == size_hints.max_width
&& size_hints.min_height == size_hints.max_height)
- *floating = true;
- }
-
- xcb_ewmh_get_atoms_reply_t win_state;
-
- if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
- for (unsigned int i = 0; i < win_state.atoms_len; i++) {
- xcb_atom_t a = win_state.atoms[i];
- if (a == ewmh->_NET_WM_STATE_FULLSCREEN)
- *fullscreen = true;
- else if (a == ewmh->_NET_WM_STATE_STICKY)
- *sticky = true;
- }
- xcb_ewmh_get_atoms_reply_wipe(&win_state);
+ csq->floating = true;
}
-
xcb_window_t transient_for = XCB_NONE;
xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
- *transient = (transient_for == XCB_NONE ? false : true);
- if (*transient)
- *floating = true;
-
- rule_t *rule = rule_head;
-
- while (rule != NULL) {
- if (is_match(rule, win)) {
- rule_effect_t efc = rule->effect;
- if (efc.floating)
- *floating = true;
- if (efc.fullscreen)
- *fullscreen = true;
- if (efc.locked)
- *locked = true;
- if (efc.sticky)
- *sticky = true;
- if (efc.follow)
- *follow = true;
- if (efc.focus)
- *takes_focus = true;
- if (efc.frame)
- *frame = true;
- if (efc.center)
- *center = true;
- if (efc.private)
- *private = private;
- if (efc.unmanage)
- *manage = false;
- if (efc.desc[0] != '\0') {
- coordinates_t ref = {*m, *d, NULL};
- coordinates_t loc;
- if (desktop_from_desc(efc.desc, &ref, &loc)) {
- *m = loc.monitor;
- *d = loc.desktop;
+ if (transient_for != XCB_NONE)
+ csq->transient = csq->floating = true;
+ char cmd[MAXLEN];
+ snprintf(cmd, sizeof(cmd), rule_command, win);
+ FILE *results = popen(cmd, "r");
+ if (results == NULL) {
+ warn("Failed to run rule command: '%s'.", cmd);
+ return;
+ }
+ char line[MAXLEN];
+ bool v;
+ while (fgets(line, sizeof(line), results) != NULL) {
+ size_t i = strlen(line) - 1;
+ while (i > 0 && isspace(line[i])) {
+ line[i] = '\0';
+ i--;
+ }
+ char *key = strtok(line, CSQ_BLK);
+ char *value = strtok(NULL, CSQ_BLK);
+ while (key != NULL && value != NULL) {
+ PRINTF("%s = %s\n", key, value);
+ if (streq("desktop", key)) {
+ coordinates_t ref = {mon, mon->desk, NULL};
+ coordinates_t trg = {NULL, NULL, NULL};
+ if (desktop_from_desc(value, &ref, &trg)) {
+ *m = trg.monitor;
+ *d = trg.desktop;
+ }
+ } else if (streq("monitor", key)) {
+ coordinates_t ref = {mon, NULL, NULL};
+ coordinates_t trg = {NULL, NULL, NULL};
+ if (monitor_from_desc(value, &ref, &trg)) {
+ *m = trg.monitor;
+ *d = trg.monitor->desk;
}
+ } else if (parse_bool(value, &v)) {
+ if (streq("floating", key))
+ csq->floating = v;
+#define SETCSQ(name) \
+ else if (streq(#name, key)) \
+ csq->name = v;
+ SETCSQ(fullscreen)
+ SETCSQ(locked)
+ SETCSQ(sticky)
+ SETCSQ(private)
+ SETCSQ(frame)
+ SETCSQ(center)
+ SETCSQ(lower)
+ SETCSQ(follow)
+ SETCSQ(manage)
+ SETCSQ(focus)
+#undef SETCSQ
+
}
+ key = strtok(NULL, CSQ_BLK);
+ value = strtok(NULL, CSQ_BLK);
}
- rule_t *next = rule->next;
- if (rule->one_shot)
- remove_rule(rule);
- rule = next;
}
-
- if (*sticky) {
+ if (csq->sticky) {
*m = mon;
*d = mon->desk;
}
}
-
-void list_rules(char *pattern, char *rsp)
-{
- char line[MAXLEN];
-
- for (rule_t *r = rule_head; r != NULL; r = r->next) {
- if (pattern != NULL && !streq(pattern, r->cause.name))
- continue;
- snprintf(line, sizeof(line), "%s", r->cause.name);
- strncat(rsp, line, REMLEN(rsp));
- if (r->effect.floating)
- strncat(rsp, " --floating", REMLEN(rsp));
- if (r->effect.fullscreen)
- 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)
- strncat(rsp, " --focus", REMLEN(rsp));
- if (r->effect.frame)
- strncat(rsp, " --frame", REMLEN(rsp));
- if (r->effect.private)
- strncat(rsp, " --private", REMLEN(rsp));
- if (r->effect.center)
- strncat(rsp, " --center", REMLEN(rsp));
- if (r->effect.unmanage)
- strncat(rsp, " --unmanage", REMLEN(rsp));
- if (r->one_shot)
- strncat(rsp, " --one-shot", REMLEN(rsp));
- if (r->effect.desc[0] != '\0') {
- snprintf(line, sizeof(line), " -d %s", r->effect.desc);
- strncat(rsp, line, REMLEN(rsp));
- }
- strncat(rsp, "\n", REMLEN(rsp));
- }
-}
#ifndef BSPWM_RULE_H
#define BSPWM_RULE_H
-#define MATCH_ALL "*"
-#define LST_SEP ","
+#define CSQ_BLK " =,"
-rule_t *make_rule(void);
-void add_rule(rule_t *r);
-void remove_rule(rule_t *r);
-void remove_rule_by_name(char *name);
-bool remove_rule_by_index(int idx);
-bool is_match(rule_t *r, xcb_window_t win);
-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 *frame, bool *private, bool *center, bool *manage);
-void list_rules(char *pattern, char *rsp);
+rule_consequence_t *make_rule_conquence(void);
+void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, rule_consequence_t *csq);
#endif
void load_settings(void)
{
+ snprintf(rule_command, sizeof(rule_command), "%s", RULE_COMMAND);
+
snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
#define WM_NAME "bspwm"
#define CONFIG_NAME WM_NAME "rc"
#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
+#define RULE_COMMAND "true"
#define FOCUSED_BORDER_COLOR "#7E7F89"
#define ACTIVE_BORDER_COLOR "#545350"
#define APPLY_FLOATING_ATOM false
#define IGNORE_EWMH_FOCUS false
+char rule_command[MAXLEN];
+
char focused_border_color[MAXLEN];
char active_border_color[MAXLEN];
char normal_border_color[MAXLEN];
};
typedef struct {
- char name[SMALEN];
-} rule_cause_t;
-
-typedef struct {
+ char desktop_desc[MAXLEN];
+ char monitor_desc[MAXLEN];
bool floating;
+ bool transient;
bool fullscreen;
bool locked;
bool sticky;
- bool follow;
- bool focus;
- bool frame;
bool private;
+ bool frame;
bool center;
- bool unmanage;
- char desc[MAXLEN];
-} rule_effect_t;
-
-typedef struct rule_t rule_t;
-struct rule_t {
- bool one_shot;
- rule_cause_t cause;
- rule_effect_t effect;
- rule_t *prev;
- rule_t *next;
-};
+ bool lower;
+ bool follow;
+ bool manage;
+ bool focus;
+} rule_consequence_t;
typedef struct {
xcb_point_t position;
if (override_redirect || locate_window(win, &loc))
return;
- bool floating = false, fullscreen = false, locked = false, sticky = false, follow = false, transient = false, takes_focus = true, frame = false, private = false, center = false, manage = true;
- handle_rules(win, &m, &d, &floating, &fullscreen, &locked, &sticky, &follow, &transient, &takes_focus, &frame, &private, ¢er, &manage);
+ rule_consequence_t *csq = make_rule_conquence();
+ handle_rules(win, &m, &d, csq);
- if (!manage) {
+ if (csq->lower)
+ window_lower(win);
+
+ if (!csq->manage) {
disable_floating_atom(win);
window_show(win);
return;
client_t *c = make_client(win);
update_floating_rectangle(c);
translate_client(monitor_from_client(c), m, c);
- if (center)
+ if (csq->center)
window_center(m, c);
- c->frame = frame;
+ c->frame = csq->frame;
xcb_icccm_get_wm_class_reply_t reply;
if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
xcb_icccm_get_wm_class_reply_wipe(&reply);
}
- if (c->transient)
- floating = true;
- floating = floating || d->floating;
+ csq->floating = csq->floating || d->floating;
node_t *n = make_node();
n->client = c;
insert_node(m, d, n, d->focus);
disable_floating_atom(c->window);
- set_floating(n, floating);
- set_locked(m, d, n, locked);
- set_sticky(m, d, n, sticky);
- set_private(m, d, n, private);
+ set_floating(n, csq->floating);
+ set_locked(m, d, n, csq->locked);
+ set_sticky(m, d, n, csq->sticky);
+ set_private(m, d, n, csq->private);
if (d->focus != NULL && d->focus->client->fullscreen)
set_fullscreen(d->focus, false);
- set_fullscreen(n, fullscreen);
- c->transient = transient;
+ set_fullscreen(n, csq->fullscreen);
+ c->transient = csq->transient;
arrange(m, d);
- bool give_focus = (takes_focus && (d == mon->desk || follow));
+ bool give_focus = (csq->focus && (d == mon->desk || csq->follow));
if (give_focus)
focus_node(m, d, n);
- else if (takes_focus)
+ else if (csq->focus)
pseudo_focus(d, n);
else
stack(n, STACK_ABOVE);
if (give_focus)
xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
+ free(csq);
num_clients++;
ewmh_set_wm_desktop(n, d);
ewmh_update_client_list();