#include "history.h"
#include "stack.h"
#include "ewmh.h"
+#include "rule.h"
#include "bspwm.h"
int main(int argc, char *argv[])
fd_set descriptors;
char socket_path[MAXLEN];
config_path[0] = '\0';
- int sock_fd, ret_fd, dpy_fd, sel, n;
+ int sock_fd, cli_fd, dpy_fd, max_fd, n;
struct sockaddr_un sock_address;
size_t rsp_len = 0;
char msg[BUFSIZ] = {0};
if (listen(sock_fd, SOMAXCONN) == -1)
err("Couldn't listen to the socket.\n");
- sel = MAX(sock_fd, dpy_fd) + 1;
-
signal(SIGPIPE, SIG_IGN);
load_settings();
run_config();
FD_ZERO(&descriptors);
FD_SET(sock_fd, &descriptors);
FD_SET(dpy_fd, &descriptors);
+ max_fd = MAX(sock_fd, dpy_fd);
+ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+ FD_SET(pr->fd, &descriptors);
+ if (pr->fd > max_fd)
+ max_fd = pr->fd;
+ }
+
+ if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
- if (select(sel, &descriptors, NULL, NULL, NULL) > 0) {
+ pending_rule_t *pr = pending_rule_head;
+ while (pr != NULL) {
+ pending_rule_t *next = pr->next;
+ if (FD_ISSET(pr->fd, &descriptors)) {
+ manage_window(pr->win, pr->csq, pr->fd);
+ remove_pending_rule(pr);
+ }
+ pr = next;
+ }
if (FD_ISSET(sock_fd, &descriptors)) {
- ret_fd = accept(sock_fd, NULL, 0);
- if (ret_fd > 0 && (n = recv(ret_fd, msg, sizeof(msg), 0)) > 0) {
+ cli_fd = accept(sock_fd, NULL, 0);
+ if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
msg[n] = '\0';
if (handle_message(msg, n, rsp)) {
rsp_len = strlen(rsp);
rsp_len = 1;
}
if (rsp_len == 1 && rsp[0] == MESSAGE_SUBSCRIBE) {
- add_subscriber(ret_fd);
+ add_subscriber(cli_fd);
} else {
- send(ret_fd, rsp, rsp_len, 0);
- close(ret_fd);
+ send(cli_fd, rsp, rsp_len, 0);
+ close(cli_fd);
}
rsp[0] = '\0';
}
free(event);
}
}
-
}
if (xcb_connection_has_error(dpy))
history_head = history_tail = history_needle = NULL;
stack_head = stack_tail = NULL;
subscribe_head = subscribe_tail = NULL;
+ pending_rule_head = pending_rule_tail = NULL;
last_motion_time = last_motion_x = last_motion_y = 0;
visible = auto_raise = sticky_still = record_history = true;
randr_base = 0;
remove_stack(stack_head);
while (subscribe_head != NULL)
remove_subscriber(subscribe_head);
+ while (pending_rule_head != NULL)
+ remove_pending_rule(pending_rule_head);
empty_history();
free(frozen_pointer);
}
stacking_list_t *stack_tail;
subscriber_list_t *subscribe_head;
subscriber_list_t *subscribe_tail;
+pending_rule_t *pending_rule_head;
+pending_rule_t *pending_rule_tail;
pointer_state_t *frozen_pointer;
xcb_window_t motion_recorder;
--- /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
+
+if not cmd then
+ os.exit(1)
+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 = {}
+
+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)
--- /dev/null
+#! /bin/dash
+
+xwinfo -cints $1 | xargs -d '\n' rulc -t
+++ /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
-
-if not cmd then
- os.exit(1)
-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 = {}
-
-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)
.\" Title: bspwm
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 11/07/2013
+.\" Date: 11/08/2013
.\" Manual: Bspwm Manual
.\" Source: Bspwm 0.8.6
.\" Language: English
.\"
-.TH "BSPWM" "1" "11/07/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
+.TH "BSPWM" "1" "11/08/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.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:
+External command used to retrieve rule consequences\&. The command will receive the the ID of the window being processed as its first argument\&. The output of that command must have the following format:
\fBkey1=value1 key2=value2 \&...\fR, where
\fBkeyN\fR
is one of
~~~~~~~~~~~~~~~
'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'.
+ External command used to retrieve rule consequences. The command will receive the the ID of the window being processed as its first argument. 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'.
'status_prefix'::
Prefix prepended to each of the status lines.
PRINTF("map request %X\n", e->window);
- manage_window(mon, mon->desk, e->window);
+ schedule_window(e->window);
}
void configure_request(xcb_generic_event_t *evt)
PRINTF("destroy notify %X\n", e->window);
- coordinates_t loc;
- if (locate_window(e->window, &loc)) {
- remove_node(loc.monitor, loc.desktop, loc.node);
- arrange(loc.monitor, loc.desktop);
- }
+ unmanage_window(e->window);
}
void unmap_notify(xcb_generic_event_t *evt)
PRINTF("unmap notify %X\n", e->window);
- coordinates_t loc;
- if (locate_window(e->window, &loc)) {
- remove_node(loc.monitor, loc.desktop, loc.node);
- arrange(loc.monitor, loc.desktop);
- }
+ unmanage_window(e->window);
}
void property_notify(xcb_generic_event_t *evt)
bspc config borderless_monocle true
bspc config gapless_monocle true
-bspc config rule_command "xwinfo -cints 0x%X | xargs -d '\n' rulc -t"
+bspc config rule_command "$(which rule_command)"
rulc -a 'class=="Gimp"' 'desktop=^8 follow=on floating=on'
rulc -a 'class=="Chromium"' 'desktop=^2'
#include <stdio.h>
#include <string.h>
-#include <ctype.h>
+#include <unistd.h>
#include "bspwm.h"
#include "ewmh.h"
#include "window.h"
rule_consequence_t *make_rule_conquence(void)
{
- rule_consequence_t *r = calloc(1, sizeof(rule_consequence_t));
- r->manage = r->focus = true;
- return r;
+ rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
+ rc->manage = rc->focus = true;
+ return rc;
}
-void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, rule_consequence_t *csq)
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
{
+ pending_rule_t *pr = malloc(sizeof(pending_rule_t));
+ pr->prev = pr->next = NULL;
+ pr->fd = fd;
+ pr->win = win;
+ pr->csq = csq;
+ return pr;
+}
+
+void add_pending_rule(pending_rule_t *pr)
+{
+ if (pr == NULL)
+ return;
+ PRINTF("add pending rule %i\n", pr->fd);
+ if (pending_rule_head == NULL) {
+ pending_rule_head = pending_rule_tail = pr;
+ } else {
+ pending_rule_tail->next = pr;
+ pr->prev = pending_rule_tail;
+ pending_rule_tail = pr;
+ }
+}
+
+void remove_pending_rule(pending_rule_t *pr)
+{
+ if (pr == NULL)
+ return;
+ PRINTF("remove pending rule %i\n", pr->fd);
+ pending_rule_t *a = pr->prev;
+ pending_rule_t *b = pr->next;
+ if (a != NULL)
+ a->next = b;
+ if (b != NULL)
+ b->prev = a;
+ if (pr == pending_rule_head)
+ pending_rule_head = b;
+ if (pr == pending_rule_tail)
+ pending_rule_tail = a;
+ close(pr->fd);
+ free(pr->csq);
+ free(pr);
+}
+void apply_rules(xcb_window_t win, rule_consequence_t *csq)
+{
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) {
xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
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;
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
+{
+ int fds[2];
+ if (pipe(fds) == -1)
+ return false;
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (dpy != NULL)
+ close(xcb_get_file_descriptor(dpy));
+ dup2(fds[1], 1);
+ close(fds[0]);
+ char wid[SMALEN];
+ snprintf(wid, sizeof(wid), "%i", win);
+ execl(rule_command, rule_command, wid, NULL);
+ err("Couldn't spawn rule command.\n");
+ } else if (pid > 0) {
+ close(fds[1]);
+ pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
+ add_pending_rule(pr);
}
- char line[MAXLEN];
+ return (pid != -1);
+}
+
+void parse_rule_consequence(int fd, rule_consequence_t *csq, monitor_t **m, desktop_t **d)
+{
+ if (fd == -1)
+ return;
+ char data[BUFSIZ];
+ int nb;
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);
+ while ((nb = read(fd, data, sizeof(data))) > 0) {
+ int end = MIN(nb, (int) sizeof(data) - 1);
+ data[end] = '\0';
+ char *key = strtok(data, CSQ_BLK);
char *value = strtok(NULL, CSQ_BLK);
while (key != NULL && value != NULL) {
PRINTF("%s = %s\n", key, value);
value = strtok(NULL, CSQ_BLK);
}
}
- pclose(results);
if (csq->sticky) {
*m = mon;
*d = mon->desk;
#ifndef BSPWM_RULE_H
#define BSPWM_RULE_H
-#define CSQ_BLK " =,"
+#define CSQ_BLK " =,\n"
rule_consequence_t *make_rule_conquence(void);
-void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, rule_consequence_t *csq);
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
+void add_pending_rule(pending_rule_t *pr);
+void remove_pending_rule(pending_rule_t *pr);
+void apply_rules(xcb_window_t win, rule_consequence_t *csq);
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
+void parse_rule_consequence(int fd, rule_consequence_t *csq, monitor_t **m, desktop_t **d);
#endif
#define WM_NAME "bspwm"
#define CONFIG_NAME WM_NAME "rc"
#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
-#define RULE_COMMAND "true"
+#define RULE_COMMAND "/bin/true"
#define STATUS_PREFIX "W"
#define FOCUSED_BORDER_COLOR "#7E7F89"
bool focus;
} rule_consequence_t;
+typedef struct pending_rule_t pending_rule_t;
+struct pending_rule_t {
+ int fd;
+ xcb_window_t win;
+ rule_consequence_t *csq;
+ pending_rule_t *prev;
+ pending_rule_t *next;
+};
+
typedef struct {
xcb_point_t position;
pointer_action_t action;
#include "tree.h"
#include "window.h"
-void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
+void schedule_window(xcb_window_t win)
{
coordinates_t loc;
- xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
uint8_t override_redirect = 0;
+ xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
if (wa != NULL) {
override_redirect = wa->override_redirect;
return;
rule_consequence_t *csq = make_rule_conquence();
- handle_rules(win, &m, &d, csq);
+ apply_rules(win, csq);
+ if (!schedule_rules(win, csq)) {
+ manage_window(win, csq, -1);
+ free(csq);
+ }
+}
+
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
+{
+ monitor_t *m = mon;
+ desktop_t *d = mon->desk;
+
+ parse_rule_consequence(fd, csq, &m, &d);
if (csq->lower)
window_lower(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();
}
+void unmanage_window(xcb_window_t win)
+{
+ coordinates_t loc;
+ if (locate_window(win, &loc)) {
+ PRINTF("unmanage %X\n", win);
+ remove_node(loc.monitor, loc.desktop, loc.node);
+ arrange(loc.monitor, loc.desktop);
+ } else {
+ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+ if (pr->win == win) {
+ remove_pending_rule(pr);
+ return;
+ }
+ }
+ }
+}
+
void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
{
if (n == NULL || (n->client->border_width < 1 && !n->client->frame)) {
for (int i = 0; i < len; i++) {
uint32_t idx;
xcb_window_t win = wins[i];
- if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
- coordinates_t loc;
- if (ewmh_locate_desktop(idx, &loc))
- manage_window(loc.monitor, loc.desktop, win);
- else
- manage_window(mon, mon->desk, win);
- }
+ if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1)
+ schedule_window(win);
}
+
free(qtr);
}
#include <xcb/xcb_icccm.h>
#include "types.h"
-void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win);
+void schedule_window(xcb_window_t win);
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
+void unmanage_window(xcb_window_t win);
void window_draw_border(node_t *n, bool focused_window, bool focused_monitor);
void draw_frame_background(node_t *n, bool focused_window, bool focused_monitor);
pointer_state_t *make_pointer_state(void);