]> git.lizzy.rs Git - bspwm.git/commitdiff
Externalize rules
authorBastien Dejean <nihilhill@gmail.com>
Tue, 5 Nov 2013 19:09:24 +0000 (20:09 +0100)
committerBastien Dejean <nihilhill@gmail.com>
Tue, 5 Nov 2013 19:09:24 +0000 (20:09 +0100)
22 files changed:
Sourcedeps
bspwm.c
bspwm.h
contrib/bash_completion
contrib/rules/README.asciidoc [new file with mode: 0644]
contrib/rules/rulc [new file with mode: 0755]
contrib/rules/ruld [new file with mode: 0755]
contrib/zsh_completion
doc/TODO.md
doc/bspwm.1
doc/bspwm.1.txt
examples/bspwmrc
history.c
messages.c
messages.h
restore.c
rule.c
rule.h
settings.c
settings.h
types.h
window.c

index a6b9f79aba7ab980a2e34442efa196782628388e..c2bcedcc2be1874e1746d10470e013d59d5b00ce 100644 (file)
@@ -1,16 +1,16 @@
 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
@@ -19,9 +19,9 @@ pointer.o: pointer.c bspwm.h types.h helpers.h query.h settings.h stack.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 \
diff --git a/bspwm.c b/bspwm.c
index a11c57f788f808fcb9611fc2b3dd415d0ff22b2d..606b4fd93102562aaa812fd7323a7ad594f140b4 100644 (file)
--- a/bspwm.c
+++ b/bspwm.c
@@ -45,7 +45,6 @@
 #include "window.h"
 #include "history.h"
 #include "stack.h"
-#include "rule.h"
 #include "ewmh.h"
 #include "bspwm.h"
 
@@ -193,7 +192,6 @@ void init(void)
     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;
@@ -296,8 +294,6 @@ void cleanup(void)
 {
     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();
diff --git a/bspwm.h b/bspwm.h
index d5c46d003313055df1554d9cb1062c25af3ad9e8..deae2eda2dd49cf21dd368e123551d773def9676 100644 (file)
--- a/bspwm.h
+++ b/bspwm.h
@@ -53,8 +53,6 @@ history_t *history_tail;
 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;
index 64b29d44cd71c30a0b2e0c9766845018a4dd86f0..2ffe109006d0a2a46bc2dcf00ad4e926a7a4a33a 100644 (file)
@@ -1,7 +1,7 @@
 _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=()
 
diff --git a/contrib/rules/README.asciidoc b/contrib/rules/README.asciidoc
new file mode 100644 (file)
index 0000000..17ae1cf
--- /dev/null
@@ -0,0 +1,42 @@
+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.
diff --git a/contrib/rules/rulc b/contrib/rules/rulc
new file mode 100755 (executable)
index 0000000..339954a
--- /dev/null
@@ -0,0 +1,64 @@
+#! /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)
diff --git a/contrib/rules/ruld b/contrib/rules/ruld
new file mode 100755 (executable)
index 0000000..cf586ab
--- /dev/null
@@ -0,0 +1,141 @@
+#! /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)
index cbd2eef84f19c0986dc8f9730cc54a7c2e8a54ea..03161767ad00ee172f750892c5173344ac49b4c3 100644 (file)
@@ -2,8 +2,8 @@
 
 _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
index 38b0cef20fb35b38bd613e5abeab19175211f82b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1 +0,0 @@
-- Externalize rules?
index 69e967504bfdc9987336fd0a2c8342a5ce57381b..71b4d2b1ffd65e3b5db68836ddf0da932261bb48 100644 (file)
@@ -2,12 +2,12 @@
 .\"     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
 .\" -----------------------------------------------------------------
@@ -834,42 +834,6 @@ Pass the pointer root coordinates for the current pointer action\&.
 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
@@ -907,6 +871,28 @@ Colors are either X color names or \fI#RRGGBB\fR, booleans are \fItrue\fR or \fI
 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\&.
index 531daf41f62c5e71b964d7c3c5282291a2afeb67..b630b9c9cab1468813e361bd11e43105674c7cb3 100644 (file)
@@ -512,26 +512,6 @@ Options
 *-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
 ~~~~~~
 
@@ -559,6 +539,9 @@ All the boolean settings are 'false' by default.
 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.
 
index a867dae54b467022ae954b5d247d82a0c4f05bf2..44faf47f0a993ec99adabb1234b2520c674478db 100755 (executable)
@@ -2,14 +2,16 @@
 
 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'
index d7782be7ac3edeeef26ab4b8a01073a95268a40a..4f6882c55def0051ca0efc3b9e081d7f96e7fca4 100644 (file)
--- a/history.c
+++ b/history.c
@@ -24,7 +24,6 @@
 
 #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)
index 62648a186e6d5c779e89e2f44b87540c18872c5f..46e7415cd0f26ba8bc44e554a45dede74ad6d455 100644 (file)
@@ -34,7 +34,6 @@
 #include "pointer.h"
 #include "query.h"
 #include "restore.h"
-#include "rule.h"
 #include "settings.h"
 #include "tree.h"
 #include "window.h"
@@ -90,8 +89,6 @@ bool process_message(char **args, int num, char *rsp)
         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)) {
@@ -630,83 +627,6 @@ bool cmd_query(char **args, int num, char *rsp)
     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)
@@ -877,6 +797,8 @@ bool set_setting(coordinates_t loc, char *name, char *value)
     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)
@@ -983,6 +905,8 @@ bool get_setting(coordinates_t loc, char *name, char* rsp)
             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) \
index 754542371383ee7bb760267fc701606451bdef18..099bae3aeffa724c9d26a2891dba364944a8946d 100644 (file)
@@ -37,7 +37,6 @@ bool cmd_window(char **args, int num);
 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);
index c18275c464cbcca885497537d9cbb1e3faa9dfca..680c6a04725a45c665c6adcb845b74180c822ff9 100644 (file)
--- a/restore.c
+++ b/restore.c
@@ -31,7 +31,6 @@
 #include "monitor.h"
 #include "window.h"
 #include "query.h"
-#include "settings.h"
 #include "stack.h"
 #include "tree.h"
 #include "restore.h"
diff --git a/rule.c b/rule.c
index d98e04a40f916103c87565b98a4aa0d86e982521..93706c58ad131db295568ca7bc7b34d268f9be46 100644 (file)
--- a/rule.c
+++ b/rule.c
 
 #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));
-    }
-}
diff --git a/rule.h b/rule.h
index 1081606738e3733dc258ba01574d653b3266fcc8..ac717ebcb516fc278f4b88b13daa1bbb4871e686 100644 (file)
--- a/rule.h
+++ b/rule.h
 #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
index 21c246350339194e4d4aae27748ff605d783cedc..72bff633af75a6099971d743a93c6227e54b68cc 100644 (file)
@@ -46,6 +46,8 @@ void run_config(void)
 
 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);
index 64e6daf973aae2b47b1c7446d35dec181b854884..73a22ac3ffc6250da7287c572f2e62d2c5a69e63 100644 (file)
@@ -30,6 +30,7 @@
 #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"
@@ -63,6 +64,8 @@
 #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];
diff --git a/types.h b/types.h
index b334e837f2214b86685cd36ade0b7ce188aa4e71..67b78c77606fee464d8e3a2c1a05dfb3ced358a1 100644 (file)
--- a/types.h
+++ b/types.h
@@ -240,31 +240,21 @@ struct stacking_list_t {
 };
 
 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;
index 3e98240898d6f488ba19cf8f5dde07d21312928a..49c155399a2fda472dae68c86ef58186665c54a3 100644 (file)
--- a/window.c
+++ b/window.c
@@ -48,10 +48,13 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
     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, &center, &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;
@@ -62,9 +65,9 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
     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) {
@@ -72,9 +75,7 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
         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;
@@ -82,24 +83,24 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
     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);
@@ -118,6 +119,7 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
     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();