]> git.lizzy.rs Git - bspwm.git/commitdiff
Rewrite message handling
authorBastien Dejean <nihilhill@gmail.com>
Mon, 8 Jul 2013 09:42:05 +0000 (11:42 +0200)
committerBastien Dejean <nihilhill@gmail.com>
Fri, 12 Jul 2013 19:52:02 +0000 (21:52 +0200)
The new message syntax:
- Provides 10 commands instead of 60.
- Allows multiple actions to be applied in one call.

The client now returns an non zero exit code when a message fails.

The `is_adjacent` function now handles vacant nodes.

33 files changed:
Makefile
README.asciidoc
Sourcedeps
bash_completion
bspc.c
bspwm.c
bspwm.h
common.h
doc/TODO.md
doc/bspwm.1
doc/bspwm.1.txt
events.c
ewmh.c
ewmh.h
examples/autostart
examples/sxhkdrc
helpers.c
helpers.h
messages.c
messages.h
query.c [new file with mode: 0644]
query.h [new file with mode: 0644]
restore.c [new file with mode: 0644]
restore.h [new file with mode: 0644]
rules.c
rules.h
settings.c
tree.c
tree.h
types.c
types.h
window.c
window.h

index c13b887075f076a11e7eec612b3da9c481ad97e4..a921dab5b1d6bc3ab3a121fdb37c2d5c37ecbe87 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ BINPREFIX = $(PREFIX)/bin
 MANPREFIX = $(PREFIX)/share/man
 CPLPREFIX = $(PREFIX)/share/bash-completion/completions
 
-WM_SRC = bspwm.c events.c messages.c ewmh.c settings.c helpers.c tree.c types.c rules.c window.c
+WM_SRC = bspwm.c helpers.c settings.c types.c tree.c events.c window.c messages.c query.c restore.c rules.c ewmh.c
 WM_OBJ = $(WM_SRC:.c=.o)
 CL_SRC = bspc.c helpers.c
 CL_OBJ = $(CL_SRC:.c=.o)
index 40a1b6bf552b0a4a59391ec2ab1769f621023b8b..67108a691943b4aef400707ab3fa9e5d0e3fab5b 100644 (file)
@@ -17,7 +17,7 @@ Synopsis
 
 *bspwm* [*-h*|*-v*|*-s* 'PANEL_FIFO'|*-p* 'PANEL_PREFIX']
 
-*bspc* 'MESSAGE' ['ARGUMENTS'] ['OPTIONS']
+*bspc* 'COMMAND' ['ARGUMENTS']
 
 Description
 -----------
@@ -26,6 +26,23 @@ Description
 
 It is controlled and configured via *bspc*.
 
+
+Options
+-------
+
+*-h*::
+    Print the synopsis and exit.
+
+*-v*::
+    Print the version and exit.
+
+*-s* 'PANEL_FIFO'::
+    Write the internal state to the given FIFO.
+
+*-p* 'PANEL_PREFIX'::
+    Start every line written to the 'PANEL_FIFO' with the given prefix.
+
+
 Configuration
 -------------
 
@@ -40,9 +57,11 @@ Example configuration files can be found in the *examples* directory.
 Splitting Modes
 ---------------
 
+New windows are inserted in the tree as close as possible to the focused window.
+
 There is only two splitting modes: 'automatic' and 'manual'.
 
-The default mode is 'automatic'. The 'manual' mode is entered by sending a *presel* message.
+The default mode is 'automatic'. The 'manual' mode is entered by sending a *preselection* message.
 
 Example: insertion of a new node (number 4) into the given tree in 'automatic' mode:
 
@@ -65,7 +84,7 @@ Example: insertion of a new node (number 4) into the given tree in 'automatic' m
  +-------------------------+         +-------------------------+
 ----
 
-Same departure, but the mode is 'manual', and a *presel* 'up' message was sent beforehand:
+Same departure, but the mode is 'manual', and a *window --presel up* message was sent beforehand:
 
 ----
              b                                   b
@@ -92,189 +111,254 @@ Each monitor contains at least one desktop.
 
 Each desktop contains at most one tree.
 
-Messages
+
+Definitions
+-----------
+
+----
+WINDOW_SEL     :=  (CYCLE|DIR|biggest|focused|last)[.WINDOW_CLASS] | <window_id>
+DESKTOP_SEL    :=  (CYCLE|focused|last)[.DESKTOP_CLASS] | <desktop_name>
+MONITOR_SEL    :=  (CYCLE|DIR|focused|last)[.DESKTOP_CLASS] | <monitor_name>
+
+DESKTOP_CLASS  :=  [occupied|free]
+WINDOW_CLASS   :=  [floating|tiled][.][like|unlike]
+
+DIR            :=  left|right|up|down
+CYCLE          :=  next|prev
+ROTATE         :=  90|270|180
+FLIP           :=  horizontal|vertical
+----
+
+Commands
 --------
 
+Window
+~~~~~~
 
-*get* 'SETTING'::
-    Return the value of the given setting.
+General Syntax
+^^^^^^^^^^^^^^
 
-*set* 'SETTING' 'VALUE'::
-    Set the value of the given setting.
+window ['WINDOW_SEL'] 'OPTIONS'
 
-*list* ['DESKTOP_NAME']::
-    Output the internal representation of the window tree.
+Options
+^^^^^^^
+*-f*, *--focus* ['WINDOW_SEL']::
+    Focus the selected or given window.
 
-*list_desktops* [*--quiet*]::
-    Perform a dump of each desktop for the current monitor.
+*-d*, *--to-desktop* 'DESKTOP_SEL'::
+    Send the selected window to the given desktop.
 
-*list_monitors* [*--quiet*]::
-    Perform a dump of each monitor.
+*-m*, *--to-monitor* 'MONITOR_SEL'::
+    Send the selected window to the given monitor.
 
-*list_history*::
-    Return the node focus history of each desktop.
+*-w*, *--to-window* 'WINDOW_SEL'::
+    Transplant the selected window to the given window.
 
-*list_windows*::
-    Return the list of managed windows (i.e. their identifiers).
+*-s*, *--swap* 'WINDOW_SEL'::
+    Swap the selected window with the given window.
 
-*list_rules*::
-    Return the list of rules.
+*-p*, *--presel* 'DIR'|cancel::
+    Preselect the splitting area of the selected window (or cancel the preselection).
 
-*presel* 'left'|'right'|'up'|'down' ['SPLIT_RATIO']::
-    Switch to manual mode and select the splitting direction.
+*-r*, *--ratio* 'RATIO'::
+    Set the splitting ratio of the selected window.
 
-*cancel* [*--all*]::
-    Switch to automatic mode.
+*-e*, *--edge* 'DIR' 'RATIO'|pull|push::
+    Set the splitting ratio (or pull, or push) the edge located in the given direction in relation to the selected window.
 
-*ratio* 'VALUE'::
-    Set the splitting ratio of the focused window.
+*-t*, *--toggle* floating|fullscreen|locked[=on|off]::
+    Set or toggle the given state for the selected window.
 
-*pad* 'MONITOR_NAME' ['TOP_PADDING' ['RIGHT_PADDING' ['BOTTOM_PADDING' ['LEFT_PADDING']]]]::
-    Set the padding of the given monitor.
+*-c*, *--close*::
+    Close the selected window.
 
-*focus* 'left'|'right'|'up'|'down'::
-    Focus the neighbor window situated in the given direction.
+*-k*, *--kill*::
+    Kill the selected window.
 
-*shift* 'left'|'right'|'up'|'down'::
-    Exchange the current window with the given neighbor.
+Desktop
+~~~~~~~
 
-*swap* [*--keep-focus*]::
-    Swap the focused window with the last focused window.
+General Syntax
+^^^^^^^^^^^^^^
 
-*push* 'left'|'right'|'up'|'down'::
-    Push the fence located in the given direction.
+desktop ['DESKTOP_SEL'] 'OPTIONS'
 
-*pull* 'left'|'right'|'up'|'down'::
-    Pull the fence located in the given direction.
+Options
+^^^^^^^
+*-f*, *--focus* ['DESKTOP_SEL']::
+    Focus the selected or given desktop.
 
-*fence_ratio* 'left'|'right'|'up'|'down' 'RATIO'::
-    Set the splitting ratio of the fence located in the given direction.
+*-m*, *--to-monitor* 'MONITOR_SEL'::
+    Send the selected desktop to the given monitor.
 
-*cycle* 'next'|'prev' [*--skip-floating*|*--skip-tiled*|*--skip-class-equal*|*--skip-class-differ*]::
-    Focus the next or previous window matching the given constraints.
+*-l*, *--layout* 'CYCLE'|monocle|tiled::
+    Set or cycle the layout of the selected desktop.
 
-*nearest* 'older'|'newer' [*--skip-floating*|*--skip-tiled*|*--skip-class-equal*|*--skip-class-differ*]::
-    Focus the nearest window matching the given constraints.
+*-n*, *--rename* <new_name>::
+    Rename the selected desktop.
 
-*biggest*::
-    Return the ID of the biggest tiled window.
+*-r*, *--remove*::
+    Remove the selected desktop.
 
-*circulate* 'forward'|'backward'::
-    Circulate the leaves in the given direction.
+*-c*, *--cancel-presel*::
+    Cancel the preselection of all the windows of the selected desktop.
 
-*grab_pointer* 'focus'|'move'|'resize_side'|'resize_corner'::
-    Begin the specified pointer action.
+*-F*, *--flip* 'FLIP'::
+    Flip the tree of the selected desktop.
 
-*track_pointer* 'ROOT_X' 'ROOT_Y'::
-    Pass the pointer root coordinates for the current pointer action.
+*-R*, *--rotate* 'ROTATE'::
+    Rotate the tree of the selected desktop.
 
-*toggle_fullscreen*::
-    Toggle the fullscreen state of the current window.
+*-B*, *--balance*::
+    Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
 
-*toggle_floating*::
-    Toggle the floating state of the current window.
+*-C*, *--circulate* forward|backward::
+    Circulate the leaves of the tree of the selected desktop.
 
-*toggle_locked*::
-    Toggle the locked state of the current window (locked windows will not respond to the *close* message).
+Monitor
+~~~~~~~
 
-*toggle_visibility*::
-    Toggle the visibility of all the managed windows.
+General Syntax
+^^^^^^^^^^^^^^
 
-*close*::
-    Close the focused window.
+monitor ['MONITOR_SEL'] 'OPTIONS'
 
-*kill*::
-    Kill the focused window.
+Options
+^^^^^^^
+*-f*, *--focus* ['MONITOR_SEL']::
+    Focus the selected or given monitor.
 
-*send_to* 'DESKTOP_NAME' [*--follow*]::
-    Send the focused window to the given desktop.
+*-a*, *--add-desktops* <name>...::
+    Create desktops with the given names in the selected monitor.
 
-*drop_to* 'next'|'prev' [*--follow*]::
-    Send the focused window to the next or previous desktop.
+*-r*, *--remove-desktops* <name>...::
+    Remove desktops with the given names.
 
-*send_to_monitor* 'MONITOR_NAME' [*--follow*]::
-    Send the focused window to the given monitor.
+*-p*, *--pad* <top> <right> <bottom> <left>::
+    Set the padding of the selected monitor.
 
-*drop_to_monitor* 'next'|'prev' [*--follow*]::
-    Send the focused window to the next or previous monitor.
+*-n*, *--rename* <new_name>::
+    Rename the selected monitor.
 
-*use* 'DESKTOP_NAME'::
-    Select the given desktop.
+Query
+~~~~~
 
-*use_monitor* 'MONITOR_NAME'::
-    Select the given monitor.
+General Syntax
+^^^^^^^^^^^^^^
 
-*focus_monitor* 'left'|'right'|'up'|'down'::
-    Focus the nearest monitor in the given direction.
+query 'OPTIONS'
 
-*alternate*::
-    Alternate between the current and the last focused window.
+Options
+^^^^^^^
+*-W*, *--windows*::
+    List matching windows.
 
-*alternate_desktop*::
-    Alternate between the current and the last focused desktop.
+*-D*, *--desktops*::
+    List matching desktops.
 
-*alternate_monitor*::
-    Alternate between the current and the last focused monitor.
+*-M*, *--monitors*::
+    List matching monitors.
 
-*add* 'DESKTOP_NAME' ...::
-    Make new desktops with the given names.
+*-T*, *--tree*::
+    Print tree rooted at query.
 
-*add_in* 'MONITOR_NAME' 'DESKTOP_NAME' ...::
-    Make new desktops with the given names in the given monitor.
+*-H*, *--history*::
+    Print the history as it relates to the query.
 
-*rename_monitor* 'CURRENT_NAME' 'NEW_NAME'::
-    Rename the monitor named 'CURRENT_NAME' to 'NEW_NAME'.
+[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]::
+    Constrain matches to the selected monitor, desktop or window.
 
-*rename* 'CURRENT_NAME' 'NEW_NAME'::
-    Rename the desktop named 'CURRENT_NAME' to 'NEW_NAME'.
+Restore
+~~~~~~~
 
-*remove_desktop* 'DESKTOP_NAME' ...::
-    Remove the given desktops.
+General Syntax
+^^^^^^^^^^^^^^
 
-*send_desktop_to* 'MONITOR_NAME' [*--follow*]::
-    Send the current desktop to the given monitor.
+restore 'OPTIONS'
+
+Options
+^^^^^^^
+
+*-T*, *--tree* <file_path>::
+    Load the desktop trees from the given file.
+
+*-H*, *--history* <file_path>::
+    Load the focus history from the given file.
+
+Control
+~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+
+control 'OPTIONS'
+
+Options
+^^^^^^^
+
+*--adopt-orphans* <file_path>::
+   Manage all the unmanaged windows remaining from a previous session.
+
+*--put-status* <file_path>::
+    Write the current internal state to the panel FIFO.
+
+*--toggle-visibility*::
+    Toggle the visibility of all the managed windows.
+
+Pointer
+~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+
+pointer 'OPTIONS'
+
+Options
+^^^^^^^
+
+*-t*, *--track* <x> <y>::
+    Pass the pointer root coordinates for the current pointer action.
 
-*cycle_monitor* 'next'|'prev'::
-    Select the next or previous monitor.
+*-g*, *--grab* focus|move|resize_side|resize_corner::
+    Perform the given pointer action.
 
-*cycle_desktop* 'next'|'prev' [*--skip-free*|*--skip-occupied*]::
-    Select the next or previous desktop.
+Rule
+~~~~
 
-*layout* 'monocle'|'tiled' ['DESKTOP_NAME' ...]::
-    Set the layout of the given desktops (current if none given).
+General Syntax
+^^^^^^^^^^^^^^
 
-*cycle_layout*::
-    Cycle the layout of the current desktop.
+rule 'OPTIONS'
 
-*rotate* 'clockwise'|'counter_clockwise'|'full_cycle'::
-    Rotate the window tree.
+Options
+^^^^^^^
 
-*flip* 'horizontal'|'vertical'::
-    Flip the window tree.
+*-a*, *--add* <pattern> [-d 'DESKTOP_SEL'] [--floating] [--follow]::
+    Create a new rule (<pattern> must match the class or instance name).
 
-*balance*::
-    Adjust the split ratios so that all windows occupy the same area.
+*-r*, *--rm* <rule_uid>...::
+    Remove the rules with the given UIDs.
 
-*rule* 'PATTERN' ['DESKTOP_NAME'] ['floating'] ['follow']::
-    Create a new rule ('PATTERN' must match the class or instance name).
+*-l*, *--list* [<pattern>]::
+    List the rules.
 
-*remove_rule* 'UID' ...::
-    Remove the rules with the given 'UIDs'.
+Config
+~~~~~~
 
-*put_status*::
-    Output the current state to the panel fifo.
+General Syntax
+^^^^^^^^^^^^^^
 
-*adopt_orphans*::
-    Manage all the unmanaged windows remaining from a previous session.
+config <key> [<value>]::
+    Get or set the value of <key>.
 
-*restore_layout* 'FILE_PATH'::
-    Restore the layout of each desktop from the content of 'FILE_PATH'.
+Quit
+~~~~
 
-*restore_history* 'FILE_PATH'::
-    Restore the history of each desktop from the content of 'FILE_PATH'.
+General Syntax
+^^^^^^^^^^^^^^
 
-*quit* ['EXIT_STATUS']::
-    Quit.
+quit [<status>]::
+    Quit with an optional exit status.
 
 Settings
 --------
@@ -376,7 +460,7 @@ Key Features
 * Configured and controlled through messages.
 * Multiple monitors support (via 'RandR').
 * EWMH support (*tint2* works).
-* Automatic and manual modes.
+* Hybrid tiling.
 
 Contributors
 ------------
index 6dd5b6655ad7d19abcde559ef40511c806511c8a..75012bf8b9b10bedeef7166e707a0e926281088b 100644 (file)
@@ -3,7 +3,9 @@ bspwm.o: bspwm.c bspwm.h common.h events.h ewmh.h helpers.h messages.h rules.h s
 events.o: events.c bspwm.h events.h ewmh.h helpers.h rules.h settings.h tree.h types.h window.h
 ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h
 helpers.o: helpers.c bspwm.h helpers.h types.h
-messages.o: messages.c bspwm.h common.h events.h ewmh.h helpers.h messages.h rules.h settings.h tree.h types.h window.h
+messages.o: messages.c bspwm.h common.h events.h ewmh.h helpers.h messages.h query.h restore.h rules.h settings.h tree.h types.h window.h
+query.o: query.c bspwm.h helpers.h query.h tree.h types.h
+restore.o: restore.c bspwm.h ewmh.h helpers.h restore.h settings.h tree.h types.h
 rules.o: rules.c bspwm.h ewmh.h helpers.h rules.h types.h
 settings.o: settings.c bspwm.h common.h helpers.h settings.h types.h
 tree.o: tree.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h window.h
index af0fee318057685818eb98137f5ec670d5571295..13adf1fb1c192753a8fa8a6988ce54bce2e5c198 100644 (file)
@@ -1,6 +1,6 @@
 _bspc()
 {
-    local messages='get set list list_desktops list_monitors list_windows list_rules list_history presel cancel ratio pad focus shift swap push pull fence_ratio cycle nearest biggest circulate grab_pointer track_pointer toggle_fullscreen toggle_floating toggle_locked toggle_visibility close kill send_to drop_to send_to_monitor drop_to_monitor use use_monitor focus_monitor alternate alternate_desktop alternate_monitor add add_in rename_monitor rename remove_desktop send_desktop_to cycle_monitor cycle_desktop layout cycle_layout rotate flip balance rule remove_rule put_status adopt_orphans restore_layout restore_history quit'
+    local messages='window desktop monitor query pointer rule restore control config quit'
 
     local settings='focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color urgent_border_color border_width window_gap split_ratio top_padding right_padding bottom_padding left_padding wm_name borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor monitor_focus_fallback adaptative_raise apply_shadow_property auto_alternate auto_cancel focus_by_distance history_aware_focus'
 
@@ -14,7 +14,7 @@ _bspc()
         else
             local second_word=${COMP_WORDS[1]}
             case $second_word in
-                set|get)
+                config)
                     if [[ $COMP_CWORD -eq 2 ]] ; then
                         COMPREPLY=( $(compgen -W "$settings" -- "$current_word") )
                     fi
diff --git a/bspc.c b/bspc.c
index ee6432d523c2337a1d9cf56e37f0956be2965b5f..c5e1697e689528ea9aaa843fe84810aadd4be03b 100644 (file)
--- a/bspc.c
+++ b/bspc.c
@@ -10,7 +10,6 @@ int main(int argc, char *argv[])
 {
     int sock_fd;
     struct sockaddr_un sock_address;
-    size_t msglen = 0;
     char msg[BUFSIZ];
     char rsp[BUFSIZ];
 
@@ -18,42 +17,46 @@ int main(int argc, char *argv[])
         err("No arguments given.\n");
 
     char *sock_path = getenv(SOCKET_ENV_VAR);
-    if (sock_path == NULL || strlen(sock_path) == 0)
-        warn("The environment variable '%s' is not set or empty, we will use: '%s'.\n", SOCKET_ENV_VAR, DEFAULT_SOCKET_PATH);
-    else if (sizeof(sock_address.sun_path) <= strlen(sock_path))
-        err("The string can't fit in the socket address: '%s'.\n", sock_path);
+    if (sock_path != NULL && sizeof(sock_address.sun_path) <= strlen(sock_path))
+        err("The socket path can't fit into the socket address.\n");
 
     sock_address.sun_family = AF_UNIX;
     strncpy(sock_address.sun_path, (sock_path == NULL ? DEFAULT_SOCKET_PATH : sock_path), sizeof(sock_address.sun_path));
     sock_address.sun_path[sizeof(sock_address.sun_path) - 1] = 0;
 
-    for (int offset = 0, len = sizeof(msg), n = 0; --argc && ++argv && len > 0; offset += n, len -= n)
-        n = snprintf(msg + offset, len, "%s ", *argv);
+    argc--, argv++;
+    int msg_len = 0;
 
-    msglen = strlen(msg);
-    if (msg[msglen - 1] == ' ')
-        msg[--msglen] = '\0';
+    for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, msg_len = offset, rem -= n, argc--, argv++) {
+        n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
+        msg_len += n;
+    }
 
-    sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (sock_fd == -1)
+    if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
         err("Failed to create the socket.\n");
 
     if (connect(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
         err("Failed to connect to the socket.\n");
 
-    if (send(sock_fd, msg, msglen, 0) == -1)
+    if (send(sock_fd, msg, msg_len, 0) == -1)
         err("Failed to send the data.\n");
 
+    int ret = EXIT_SUCCESS;
+
     int n = recv(sock_fd, rsp, sizeof(rsp), 0);
     if (n == -1) {
         err("Failed to get the response.\n");
     } else if (n > 0) {
-        rsp[n] = '\0';
-        printf("%s\n", rsp);
+        if (n == 1 && rsp[0] == MESSAGE_FAILURE) {
+            ret = EXIT_FAILURE;
+        } else {
+            rsp[n] = '\0';
+            printf("%s\n", rsp);
+        }
     }
 
     if (sock_fd)
         close(sock_fd);
 
-    return EXIT_SUCCESS;
+    return ret;
 }
diff --git a/bspwm.c b/bspwm.c
index 249a7d8d35c477be920e68424690091ada8b6b6e..c6ca5e056a836e4f5c6b69422d23867d3487119b 100644 (file)
--- a/bspwm.c
+++ b/bspwm.c
@@ -7,8 +7,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
 #include <xcb/xcb.h>
 #include <xcb/xcb_event.h>
 #include <xcb/xcb_ewmh.h>
 #include "tree.h"
 #include "ewmh.h"
 
-void quit(void)
-{
-    running = false;
-}
-
-void cleanup(void)
-{
-    while (mon_head != NULL)
-        remove_monitor(mon_head);
-    while (rule_head != NULL)
-        remove_rule(rule_head);
-    free(frozen_pointer);
-}
-
-void register_events(void)
-{
-    uint32_t values[] = {ROOT_EVENT_MASK};
-    xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
-    if (e != NULL) {
-        xcb_disconnect(dpy);
-        err("Another window manager is already running.\n");
-    }
-}
-
-bool import_monitors(void)
-{
-    PUTS("import monitors");
-    xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
-    if (sres == NULL)
-        return false;
-
-    int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
-    xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
-
-    xcb_randr_get_output_info_cookie_t cookies[len];
-    for (int i = 0; i < len; i++)
-        cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
-
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        m->wired = false;
-
-    monitor_t *mm = NULL;
-    unsigned int num = 0;
-
-    for (int i = 0; i < len; i++) {
-        xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
-        if (info != NULL && info->crtc != XCB_NONE) {
-
-            xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
-            if (cir != NULL) {
-                xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
-                mm = get_monitor_by_id(outputs[i]);
-                if (mm != NULL) {
-                    mm->rectangle = rect;
-                    for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
-                        for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-                            fit_monitor(mm, n->client);
-                    arrange(mm, mm->desk);
-                    mm->wired = true;
-                    PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
-                } else {
-                    mm = add_monitor(&rect);
-                    char *name = (char *)xcb_randr_get_output_info_name(info);
-                    size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info));
-                    strncpy(mm->name, name, name_len);
-                    mm->name[name_len] = '\0';
-                    mm->id = outputs[i];
-                    add_desktop(mm, make_desktop(NULL));
-                    PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
-                }
-                num++;
-            }
-            free(cir);
-        }
-        free(info);
-    }
-
-    monitor_t *m = mon_head;
-    while (m != NULL) {
-        monitor_t *next = m->next;
-        if (!m->wired) {
-            PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
-            merge_monitors(m, mm);
-            remove_monitor(m);
-        }
-        m = next;
-    }
-
-    free(sres);
-    update_motion_recorder();
-    return (num_monitors > 0);
-}
-
-void init(void)
-{
-    num_monitors = num_desktops = num_clients = 0;
-    monitor_uid = desktop_uid = client_uid = rule_uid = 0;
-    mon = last_mon = mon_head = mon_tail = NULL;
-    rule_head = rule_tail = NULL;
-    status_fifo = NULL;
-    randr_base = 0;
-    visible = true;
-    exit_status = 0;
-}
-
-void setup(void)
-{
-    init();
-    ewmh_init();
-    screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
-    if (screen == NULL)
-        err("Can't acquire the default screen.\n");
-    root = screen->root;
-    register_events();
-
-    screen_width = screen->width_in_pixels;
-    screen_height = screen->height_in_pixels;
-    root_depth = screen->root_depth;
-
-    uint32_t mask = XCB_CW_EVENT_MASK;
-    uint32_t values[] = {XCB_EVENT_MASK_POINTER_MOTION};
-    motion_recorder = xcb_generate_id(dpy);
-    xcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder, root, 0, 0, screen_width, screen_height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values);
-
-    xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
-                              ewmh->_NET_DESKTOP_NAMES,
-                              ewmh->_NET_NUMBER_OF_DESKTOPS,
-                              ewmh->_NET_CURRENT_DESKTOP,
-                              ewmh->_NET_CLIENT_LIST,
-                              ewmh->_NET_ACTIVE_WINDOW,
-                              ewmh->_NET_WM_DESKTOP,
-                              ewmh->_NET_WM_STATE,
-                              ewmh->_NET_WM_STATE_FULLSCREEN,
-                              ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
-                              ewmh->_NET_WM_WINDOW_TYPE,
-                              ewmh->_NET_WM_WINDOW_TYPE_DOCK,
-                              ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
-                              ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
-                              ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
-                              ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
-
-    xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
-
-    xcb_intern_atom_reply_t *iar = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen("_COMPTON_SHADOW"), "_COMPTON_SHADOW"), NULL);
-
-    if (iar != NULL) {
-        compton_shadow = iar->atom;
-        free(iar);
-    }
-
-    const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
-    if (qep->present && import_monitors()) {
-        randr = true;
-        randr_base = qep->first_event;
-        xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
-    } else {
-        randr = false;
-        warn("Couldn't retrieve monitors via RandR.\n");
-        xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-        monitor_t *m = add_monitor(&rect);
-        add_desktop(m, make_desktop(NULL));
-    }
-
-    ewmh_update_number_of_desktops();
-    ewmh_update_desktop_names();
-    ewmh_update_current_desktop();
-    frozen_pointer = make_pointer_state();
-}
-
 int main(int argc, char *argv[])
 {
     fd_set descriptors;
@@ -202,7 +31,7 @@ int main(int argc, char *argv[])
     status_prefix = NULL;
     int sock_fd, ret_fd, dpy_fd, sel, n;
     struct sockaddr_un sock_address;
-    size_t rsplen = 0;
+    size_t rsp_len = 0;
     char msg[BUFSIZ] = {0};
     char rsp[BUFSIZ] = {0};
     xcb_generic_event_t *event;
@@ -283,11 +112,15 @@ int main(int argc, char *argv[])
                 ret_fd = accept(sock_fd, NULL, 0);
                 if (ret_fd > 0 && (n = recv(ret_fd, msg, sizeof(msg), 0)) > 0) {
                     msg[n] = '\0';
-                    process_message(msg, rsp);
-                    rsplen = strlen(rsp);
-                    if (rsp[rsplen - 1] == '\n')
-                        rsp[--rsplen] = '\0';
-                    send(ret_fd, rsp, rsplen, 0);
+                    if (handle_message(msg, n, rsp)) {
+                        rsp_len = strlen(rsp);
+                        if (rsp[rsp_len - 1] == '\n')
+                            rsp[--rsp_len] = '\0';
+                    } else {
+                        rsp[0] = MESSAGE_FAILURE;
+                        rsp_len = 1;
+                    }
+                    send(ret_fd, rsp, rsp_len, 0);
                     close(ret_fd);
                     rsp[0] = '\0';
                 }
@@ -317,3 +150,193 @@ int main(int argc, char *argv[])
     xcb_disconnect(dpy);
     return exit_status;
 }
+
+void init(void)
+{
+    num_monitors = num_desktops = num_clients = 0;
+    monitor_uid = desktop_uid = rule_uid = 0;
+    mon = last_mon = mon_head = mon_tail = NULL;
+    rule_head = rule_tail = NULL;
+    status_fifo = NULL;
+    randr_base = 0;
+    visible = true;
+    exit_status = 0;
+}
+
+void setup(void)
+{
+    init();
+    ewmh_init();
+    screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
+    if (screen == NULL)
+        err("Can't acquire the default screen.\n");
+    root = screen->root;
+    register_events();
+
+    screen_width = screen->width_in_pixels;
+    screen_height = screen->height_in_pixels;
+    root_depth = screen->root_depth;
+
+    uint32_t mask = XCB_CW_EVENT_MASK;
+    uint32_t values[] = {XCB_EVENT_MASK_POINTER_MOTION};
+    motion_recorder = xcb_generate_id(dpy);
+    xcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder, root, 0, 0, screen_width, screen_height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values);
+
+    xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
+                              ewmh->_NET_DESKTOP_NAMES,
+                              ewmh->_NET_NUMBER_OF_DESKTOPS,
+                              ewmh->_NET_CURRENT_DESKTOP,
+                              ewmh->_NET_CLIENT_LIST,
+                              ewmh->_NET_ACTIVE_WINDOW,
+                              ewmh->_NET_WM_DESKTOP,
+                              ewmh->_NET_WM_STATE,
+                              ewmh->_NET_WM_STATE_FULLSCREEN,
+                              ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
+                              ewmh->_NET_WM_WINDOW_TYPE,
+                              ewmh->_NET_WM_WINDOW_TYPE_DOCK,
+                              ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
+                              ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
+                              ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
+                              ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
+
+    xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
+
+    xcb_intern_atom_reply_t *iar = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen("_COMPTON_SHADOW"), "_COMPTON_SHADOW"), NULL);
+
+    if (iar != NULL) {
+        compton_shadow = iar->atom;
+        free(iar);
+    }
+
+    const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
+    if (qep->present && import_monitors()) {
+        randr = true;
+        randr_base = qep->first_event;
+        xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
+    } else {
+        randr = false;
+        warn("Couldn't retrieve monitors via RandR.\n");
+        xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+        monitor_t *m = add_monitor(&rect);
+        add_desktop(m, make_desktop(NULL));
+    }
+
+    ewmh_update_number_of_desktops();
+    ewmh_update_desktop_names();
+    ewmh_update_current_desktop();
+    frozen_pointer = make_pointer_state();
+}
+
+void register_events(void)
+{
+    uint32_t values[] = {ROOT_EVENT_MASK};
+    xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
+    if (e != NULL) {
+        xcb_disconnect(dpy);
+        err("Another window manager is already running.\n");
+    }
+}
+
+bool import_monitors(void)
+{
+    PUTS("import monitors");
+    xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
+    if (sres == NULL)
+        return false;
+
+    int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
+    xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
+
+    xcb_randr_get_output_info_cookie_t cookies[len];
+    for (int i = 0; i < len; i++)
+        cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
+
+    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+        m->wired = false;
+
+    monitor_t *mm = NULL;
+    unsigned int num = 0;
+
+    for (int i = 0; i < len; i++) {
+        xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
+        if (info != NULL && info->crtc != XCB_NONE) {
+
+            xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
+            if (cir != NULL) {
+                xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
+                mm = get_monitor_by_id(outputs[i]);
+                if (mm != NULL) {
+                    mm->rectangle = rect;
+                    for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
+                        for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+                            fit_monitor(mm, n->client);
+                    arrange(mm, mm->desk);
+                    mm->wired = true;
+                    PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
+                } else {
+                    mm = add_monitor(&rect);
+                    char *name = (char *)xcb_randr_get_output_info_name(info);
+                    size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info));
+                    strncpy(mm->name, name, name_len);
+                    mm->name[name_len] = '\0';
+                    mm->id = outputs[i];
+                    add_desktop(mm, make_desktop(NULL));
+                    PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
+                }
+                num++;
+            }
+            free(cir);
+        }
+        free(info);
+    }
+
+    monitor_t *m = mon_head;
+    while (m != NULL) {
+        monitor_t *next = m->next;
+        if (!m->wired) {
+            PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
+            merge_monitors(m, mm);
+            remove_monitor(m);
+        }
+        m = next;
+    }
+
+    free(sres);
+    update_motion_recorder();
+    return (num_monitors > 0);
+}
+
+void quit(void)
+{
+    running = false;
+}
+
+void cleanup(void)
+{
+    while (mon_head != NULL)
+        remove_monitor(mon_head);
+    while (rule_head != NULL)
+        remove_rule(rule_head);
+    free(frozen_pointer);
+}
+
+void put_status(void)
+{
+    if (status_fifo == NULL)
+        return;
+    if (status_prefix != NULL)
+        fprintf(status_fifo, "%s", status_prefix);
+    bool urgent = false;
+    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+        fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
+        for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
+            for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
+                urgent |= n->client->urgent;
+            fprintf(status_fifo, "%c%s:", m->desk == d ? (urgent ? 'U' : 'D') : (d->root == NULL ? 'E' : (urgent ? 'u' : 'd')), d->name);
+        }
+    }
+    if (mon != NULL && mon->desk != NULL)
+        fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+    fprintf(status_fifo, "\n");
+    fflush(status_fifo);
+}
diff --git a/bspwm.h b/bspwm.h
index cdee908ea199b7bdd0ed2d85f57f423c06136d2e..a3205974d12668effe246b962abf737046a0736a 100644 (file)
--- a/bspwm.h
+++ b/bspwm.h
@@ -14,7 +14,6 @@ uint32_t num_desktops;
 unsigned int num_monitors;
 unsigned int monitor_uid;
 unsigned int desktop_uid;
-unsigned int client_uid;
 unsigned int rule_uid;
 xcb_screen_t *screen;
 xcb_window_t root;
@@ -43,6 +42,7 @@ void register_events(void);
 bool import_monitors(void);
 void init(void);
 void setup(void);
+void put_status(void);
 void cleanup(void);
 void quit(void);
 
index 1f3a06e9738a66387ebd0045555589a323a15030..f20fa6be3d32fa94368313aeefe3e4b5960d8dcb 100644 (file)
--- a/common.h
+++ b/common.h
@@ -3,5 +3,6 @@
 
 #define DEFAULT_SOCKET_PATH  "/tmp/bspwm-socket"
 #define SOCKET_ENV_VAR       "BSPWM_SOCKET"
+#define MESSAGE_FAILURE      '\x18'
 
 #endif
index 2eda6cb79e1ea5a2d4833557efe1c131b9c15fde..38b0cef20fb35b38bd613e5abeab19175211f82b 100644 (file)
@@ -1,2 +1 @@
 - Externalize rules?
-- Use macros in `messages.c`.
index 99895db5ff5bff414cb64e1593b3b7b35de06da0..51d1dec94be6c31ee3df4d4314686ba58de3237b 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: 07/05/2013
+.\"      Date: 07/12/2013
 .\"    Manual: Bspwm Manual
 .\"    Source: Bspwm 0.7
 .\"  Language: English
 .\"
-.TH "BSPWM" "1" "07/05/2013" "Bspwm 0\&.7" "Bspwm Manual"
+.TH "BSPWM" "1" "07/12/2013" "Bspwm 0\&.7" "Bspwm Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -33,12 +33,35 @@ bspwm \- Tiling window manager based on binary space partitioning
 .sp
 \fBbspwm\fR [\fB\-h\fR|\fB\-v\fR|\fB\-s\fR \fIPANEL_FIFO\fR|\fB\-p\fR \fIPANEL_PREFIX\fR]
 .sp
-\fBbspc\fR \fIMESSAGE\fR [\fIARGUMENTS\fR] [\fIOPTIONS\fR]
+\fBbspc\fR \fICOMMAND\fR [\fIARGUMENTS\fR]
 .SH "DESCRIPTION"
 .sp
 \fBbspwm\fR is a tiling window manager that represents windows as the leaves of a full binary tree\&.
 .sp
 It is controlled and configured via \fBbspc\fR\&.
+.SH "OPTIONS"
+.PP
+\fB\-h\fR
+.RS 4
+Print the synopsis and exit\&.
+.RE
+.PP
+\fB\-v\fR
+.RS 4
+Print the version and exit\&.
+.RE
+.PP
+\fB\-s\fR \fIPANEL_FIFO\fR
+.RS 4
+Write the internal state to the given FIFO\&.
+.RE
+.PP
+\fB\-p\fR \fIPANEL_PREFIX\fR
+.RS 4
+Start every line written to the
+\fIPANEL_FIFO\fR
+with the given prefix\&.
+.RE
 .SH "CONFIGURATION"
 .sp
 \fBbspwm\fR have only two sources of informations: the X events it receives and the messages it reads on a dedicated socket\&.
@@ -50,9 +73,11 @@ Keyboard and pointer bindings are defined with sxhkd\&.
 Example configuration files can be found in the \fBexamples\fR directory\&.
 .SH "SPLITTING MODES"
 .sp
+New windows are inserted in the tree as close as possible to the focused window\&.
+.sp
 There is only two splitting modes: \fIautomatic\fR and \fImanual\fR\&.
 .sp
-The default mode is \fIautomatic\fR\&. The \fImanual\fR mode is entered by sending a \fBpresel\fR message\&.
+The default mode is \fIautomatic\fR\&. The \fImanual\fR mode is entered by sending a \fBpreselection\fR message\&.
 .sp
 Example: insertion of a new node (number 4) into the given tree in \fIautomatic\fR mode:
 .sp
@@ -81,7 +106,7 @@ Example: insertion of a new node (number 4) into the given tree in \fIautomatic\
 .RE
 .\}
 .sp
-Same departure, but the mode is \fImanual\fR, and a \fBpresel\fR \fIup\fR message was sent beforehand:
+Same departure, but the mode is \fImanual\fR, and a \fBwindow \-\-presel up\fR message was sent beforehand:
 .sp
 .if n \{\
 .RS 4
@@ -111,318 +136,435 @@ Same departure, but the mode is \fImanual\fR, and a \fBpresel\fR \fIup\fR messag
 Each monitor contains at least one desktop\&.
 .sp
 Each desktop contains at most one tree\&.
-.SH "MESSAGES"
-.PP
-\fBget\fR \fISETTING\fR
+.SH "DEFINITIONS"
+.sp
+.if n \{\
 .RS 4
-Return the value of the given setting\&.
+.\}
+.nf
+WINDOW_SEL     :=  (CYCLE|DIR|biggest|focused|last)[\&.WINDOW_CLASS] | <window_id>
+DESKTOP_SEL    :=  (CYCLE|focused|last)[\&.DESKTOP_CLASS] | <desktop_name>
+MONITOR_SEL    :=  (CYCLE|DIR|focused|last)[\&.DESKTOP_CLASS] | <monitor_name>
+
+DESKTOP_CLASS  :=  [occupied|free]
+WINDOW_CLASS   :=  [floating|tiled][\&.][like|unlike]
+
+DIR            :=  left|right|up|down
+CYCLE          :=  next|prev
+ROTATE         :=  90|270|180
+FLIP           :=  horizontal|vertical
+.fi
+.if n \{\
 .RE
-.PP
-\fBset\fR \fISETTING\fR \fIVALUE\fR
+.\}
+.SH "COMMANDS"
+.SS "Window"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Set the value of the given setting\&.
+.sp
+window [\fIWINDOW_SEL\fR] \fIOPTIONS\fR
 .RE
-.PP
-\fBlist\fR [\fIDESKTOP_NAME\fR]
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBOptions\fR
 .RS 4
-Output the internal representation of the window tree\&.
-.RE
 .PP
-\fBlist_desktops\fR [\fB\-\-quiet\fR]
+\fB\-f\fR, \fB\-\-focus\fR [\fIWINDOW_SEL\fR]
 .RS 4
-Perform a dump of each desktop for the current monitor\&.
+Focus the selected or given window\&.
 .RE
 .PP
-\fBlist_monitors\fR [\fB\-\-quiet\fR]
+\fB\-d\fR, \fB\-\-to\-desktop\fR \fIDESKTOP_SEL\fR
 .RS 4
-Perform a dump of each monitor\&.
+Send the selected window to the given desktop\&.
 .RE
 .PP
-\fBlist_history\fR
+\fB\-m\fR, \fB\-\-to\-monitor\fR \fIMONITOR_SEL\fR
 .RS 4
-Return the node focus history of each desktop\&.
+Send the selected window to the given monitor\&.
 .RE
 .PP
-\fBlist_windows\fR
+\fB\-w\fR, \fB\-\-to\-window\fR \fIWINDOW_SEL\fR
 .RS 4
-Return the list of managed windows (i\&.e\&. their identifiers)\&.
+Transplant the selected window to the given window\&.
 .RE
 .PP
-\fBlist_rules\fR
+\fB\-s\fR, \fB\-\-swap\fR \fIWINDOW_SEL\fR
 .RS 4
-Return the list of rules\&.
+Swap the selected window with the given window\&.
 .RE
 .PP
-\fBpresel\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR [\fISPLIT_RATIO\fR]
+\fB\-p\fR, \fB\-\-presel\fR \fIDIR\fR|cancel
 .RS 4
-Switch to manual mode and select the splitting direction\&.
+Preselect the splitting area of the selected window (or cancel the preselection)\&.
 .RE
 .PP
-\fBcancel\fR [\fB\-\-all\fR]
+\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR
 .RS 4
-Switch to automatic mode\&.
+Set the splitting ratio of the selected window\&.
 .RE
 .PP
-\fBratio\fR \fIVALUE\fR
+\fB\-e\fR, \fB\-\-edge\fR \fIDIR\fR \fIRATIO\fR|pull|push
 .RS 4
-Set the splitting ratio of the focused window\&.
+Set the splitting ratio (or pull, or push) the edge located in the given direction in relation to the selected window\&.
 .RE
 .PP
-\fBpad\fR \fIMONITOR_NAME\fR [\fITOP_PADDING\fR [\fIRIGHT_PADDING\fR [\fIBOTTOM_PADDING\fR [\fILEFT_PADDING\fR]]]]
+\fB\-t\fR, \fB\-\-toggle\fR floating|fullscreen|locked[=on|off]
 .RS 4
-Set the padding of the given monitor\&.
+Set or toggle the given state for the selected window\&.
 .RE
 .PP
-\fBfocus\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR
+\fB\-c\fR, \fB\-\-close\fR
 .RS 4
-Focus the neighbor window situated in the given direction\&.
+Close the selected window\&.
 .RE
 .PP
-\fBshift\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR
+\fB\-k\fR, \fB\-\-kill\fR
 .RS 4
-Exchange the current window with the given neighbor\&.
+Kill the selected window\&.
 .RE
-.PP
-\fBswap\fR [\fB\-\-keep\-focus\fR]
-.RS 4
-Swap the focused window with the last focused window\&.
 .RE
-.PP
-\fBpush\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR
+.SS "Desktop"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Push the fence located in the given direction\&.
+.sp
+desktop [\fIDESKTOP_SEL\fR] \fIOPTIONS\fR
 .RE
-.PP
-\fBpull\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBOptions\fR
 .RS 4
-Pull the fence located in the given direction\&.
-.RE
 .PP
-\fBfence_ratio\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR \fIRATIO\fR
+\fB\-f\fR, \fB\-\-focus\fR [\fIDESKTOP_SEL\fR]
 .RS 4
-Set the splitting ratio of the fence located in the given direction\&.
+Focus the selected or given desktop\&.
 .RE
 .PP
-\fBcycle\fR \fInext\fR|\fIprev\fR [\fB\-\-skip\-floating\fR|\fB\-\-skip\-tiled\fR|\fB\-\-skip\-class\-equal\fR|\fB\-\-skip\-class\-differ\fR]
+\fB\-m\fR, \fB\-\-to\-monitor\fR \fIMONITOR_SEL\fR
 .RS 4
-Focus the next or previous window matching the given constraints\&.
+Send the selected desktop to the given monitor\&.
 .RE
 .PP
-\fBnearest\fR \fIolder\fR|\fInewer\fR [\fB\-\-skip\-floating\fR|\fB\-\-skip\-tiled\fR|\fB\-\-skip\-class\-equal\fR|\fB\-\-skip\-class\-differ\fR]
+\fB\-l\fR, \fB\-\-layout\fR \fICYCLE\fR|monocle|tiled
 .RS 4
-Focus the nearest window matching the given constraints\&.
+Set or cycle the layout of the selected desktop\&.
 .RE
 .PP
-\fBbiggest\fR
+\fB\-n\fR, \fB\-\-rename\fR <new_name>
 .RS 4
-Return the ID of the biggest tiled window\&.
+Rename the selected desktop\&.
 .RE
 .PP
-\fBcirculate\fR \fIforward\fR|\fIbackward\fR
+\fB\-r\fR, \fB\-\-remove\fR
 .RS 4
-Circulate the leaves in the given direction\&.
+Remove the selected desktop\&.
 .RE
 .PP
-\fBgrab_pointer\fR \fIfocus\fR|\fImove\fR|\fIresize_side\fR|\fIresize_corner\fR
+\fB\-c\fR, \fB\-\-cancel\-presel\fR
 .RS 4
-Begin the specified pointer action\&.
+Cancel the preselection of all the windows of the selected desktop\&.
 .RE
 .PP
-\fBtrack_pointer\fR \fIROOT_X\fR \fIROOT_Y\fR
+\fB\-F\fR, \fB\-\-flip\fR \fIFLIP\fR
 .RS 4
-Pass the pointer root coordinates for the current pointer action\&.
+Flip the tree of the selected desktop\&.
 .RE
 .PP
-\fBtoggle_fullscreen\fR
+\fB\-R\fR, \fB\-\-rotate\fR \fIROTATE\fR
 .RS 4
-Toggle the fullscreen state of the current window\&.
+Rotate the tree of the selected desktop\&.
 .RE
 .PP
-\fBtoggle_floating\fR
+\fB\-B\fR, \fB\-\-balance\fR
 .RS 4
-Toggle the floating state of the current window\&.
+Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area\&.
 .RE
 .PP
-\fBtoggle_locked\fR
+\fB\-C\fR, \fB\-\-circulate\fR forward|backward
 .RS 4
-Toggle the locked state of the current window (locked windows will not respond to the
-\fBclose\fR
-message)\&.
+Circulate the leaves of the tree of the selected desktop\&.
 .RE
-.PP
-\fBtoggle_visibility\fR
-.RS 4
-Toggle the visibility of all the managed windows\&.
 .RE
-.PP
-\fBclose\fR
+.SS "Monitor"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Close the focused window\&.
+.sp
+monitor [\fIMONITOR_SEL\fR] \fIOPTIONS\fR
 .RE
-.PP
-\fBkill\fR
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBOptions\fR
 .RS 4
-Kill the focused window\&.
-.RE
 .PP
-\fBsend_to\fR \fIDESKTOP_NAME\fR [\fB\-\-follow\fR]
+\fB\-f\fR, \fB\-\-focus\fR [\fIMONITOR_SEL\fR]
 .RS 4
-Send the focused window to the given desktop\&.
+Focus the selected or given monitor\&.
 .RE
 .PP
-\fBdrop_to\fR \fInext\fR|\fIprev\fR [\fB\-\-follow\fR]
+\fB\-a\fR, \fB\-\-add\-desktops\fR <name>\&...
 .RS 4
-Send the focused window to the next or previous desktop\&.
+Create desktops with the given names in the selected monitor\&.
 .RE
 .PP
-\fBsend_to_monitor\fR \fIMONITOR_NAME\fR [\fB\-\-follow\fR]
+\fB\-r\fR, \fB\-\-remove\-desktops\fR <name>\&...
 .RS 4
-Send the focused window to the given monitor\&.
+Remove desktops with the given names\&.
 .RE
 .PP
-\fBdrop_to_monitor\fR \fInext\fR|\fIprev\fR [\fB\-\-follow\fR]
+\fB\-p\fR, \fB\-\-pad\fR <top> <right> <bottom> <left>
 .RS 4
-Send the focused window to the next or previous monitor\&.
+Set the padding of the selected monitor\&.
 .RE
 .PP
-\fBuse\fR \fIDESKTOP_NAME\fR
+\fB\-n\fR, \fB\-\-rename\fR <new_name>
 .RS 4
-Select the given desktop\&.
+Rename the selected monitor\&.
 .RE
-.PP
-\fBuse_monitor\fR \fIMONITOR_NAME\fR
-.RS 4
-Select the given monitor\&.
 .RE
-.PP
-\fBfocus_monitor\fR \fIleft\fR|\fIright\fR|\fIup\fR|\fIdown\fR
+.SS "Query"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Focus the nearest monitor in the given direction\&.
+.sp
+query \fIOPTIONS\fR
 .RE
-.PP
-\fBalternate\fR
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBOptions\fR
 .RS 4
-Alternate between the current and the last focused window\&.
-.RE
 .PP
-\fBalternate_desktop\fR
+\fB\-W\fR, \fB\-\-windows\fR
 .RS 4
-Alternate between the current and the last focused desktop\&.
+List matching windows\&.
 .RE
 .PP
-\fBalternate_monitor\fR
+\fB\-D\fR, \fB\-\-desktops\fR
 .RS 4
-Alternate between the current and the last focused monitor\&.
+List matching desktops\&.
 .RE
 .PP
-\fBadd\fR \fIDESKTOP_NAME\fR \&...
+\fB\-M\fR, \fB\-\-monitors\fR
 .RS 4
-Make new desktops with the given names\&.
+List matching monitors\&.
 .RE
 .PP
-\fBadd_in\fR \fIMONITOR_NAME\fR \fIDESKTOP_NAME\fR \&...
+\fB\-T\fR, \fB\-\-tree\fR
 .RS 4
-Make new desktops with the given names in the given monitor\&.
+Print tree rooted at query\&.
 .RE
 .PP
-\fBrename_monitor\fR \fICURRENT_NAME\fR \fINEW_NAME\fR
+\fB\-H\fR, \fB\-\-history\fR
 .RS 4
-Rename the monitor named
-\fICURRENT_NAME\fR
-to
-\fINEW_NAME\fR\&.
+Print the history as it relates to the query\&.
 .RE
 .PP
-\fBrename\fR \fICURRENT_NAME\fR \fINEW_NAME\fR
+[\fB\-m\fR,\fB\-\-monitor\fR [\fIMONITOR_SEL\fR]] | [\fB\-d\fR,\fB\-\-desktop\fR [\fIDESKTOP_SEL\fR]] | [\fB\-w\fR, \fB\-\-window\fR [\fIWINDOW_SEL\fR]]
 .RS 4
-Rename the desktop named
-\fICURRENT_NAME\fR
-to
-\fINEW_NAME\fR\&.
+Constrain matches to the selected monitor, desktop or window\&.
 .RE
-.PP
-\fBremove_desktop\fR \fIDESKTOP_NAME\fR \&...
+.RE
+.SS "Restore"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Remove the given desktops\&.
+.sp
+restore \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
-\fBsend_desktop_to\fR \fIMONITOR_NAME\fR [\fB\-\-follow\fR]
+\fB\-T\fR, \fB\-\-tree\fR <file_path>
 .RS 4
-Send the current desktop to the given monitor\&.
+Load the desktop trees from the given file\&.
 .RE
 .PP
-\fBcycle_monitor\fR \fInext\fR|\fIprev\fR
+\fB\-H\fR, \fB\-\-history\fR <file_path>
 .RS 4
-Select the next or previous monitor\&.
+Load the focus history from the given file\&.
 .RE
-.PP
-\fBcycle_desktop\fR \fInext\fR|\fIprev\fR [\fB\-\-skip\-free\fR|\fB\-\-skip\-occupied\fR]
+.RE
+.SS "Control"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Select the next or previous desktop\&.
+.sp
+control \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
-\fBlayout\fR \fImonocle\fR|\fItiled\fR [\fIDESKTOP_NAME\fR \&...]
+\fB\-\-adopt\-orphans\fR <file_path>
 .RS 4
-Set the layout of the given desktops (current if none given)\&.
+Manage all the unmanaged windows remaining from a previous session\&.
 .RE
 .PP
-\fBcycle_layout\fR
+\fB\-\-put\-status\fR <file_path>
 .RS 4
-Cycle the layout of the current desktop\&.
+Write the current internal state to the panel FIFO\&.
 .RE
 .PP
-\fBrotate\fR \fIclockwise\fR|\fIcounter_clockwise\fR|\fIfull_cycle\fR
+\fB\-\-toggle\-visibility\fR
 .RS 4
-Rotate the window tree\&.
+Toggle the visibility of all the managed windows\&.
 .RE
-.PP
-\fBflip\fR \fIhorizontal\fR|\fIvertical\fR
+.RE
+.SS "Pointer"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
 .RS 4
-Flip the window tree\&.
+.sp
+pointer \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
-\fBbalance\fR
+\fB\-t\fR, \fB\-\-track\fR <x> <y>
 .RS 4
-Adjust the split ratios so that all windows occupy the same area\&.
+Pass the pointer root coordinates for the current pointer action\&.
 .RE
 .PP
-\fBrule\fR \fIPATTERN\fR [\fIDESKTOP_NAME\fR] [\fIfloating\fR] [\fIfollow\fR]
+\fB\-g\fR, \fB\-\-grab\fR focus|move|resize_side|resize_corner
 .RS 4
-Create a new rule (\fIPATTERN\fR
-must match the class or instance name)\&.
+Perform the given pointer action\&.
 .RE
-.PP
-\fBremove_rule\fR \fIUID\fR \&...
+.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
-Remove the rules with the given
-\fIUIDs\fR\&.
+.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
-\fBput_status\fR
+\fB\-a\fR, \fB\-\-add\fR <pattern> [\-d \fIDESKTOP_SEL\fR] [\-\-floating] [\-\-follow]
 .RS 4
-Output the current state to the panel fifo\&.
+Create a new rule (<pattern> must match the class or instance name)\&.
 .RE
 .PP
-\fBadopt_orphans\fR
+\fB\-r\fR, \fB\-\-rm\fR <rule_uid>\&...
 .RS 4
-Manage all the unmanaged windows remaining from a previous session\&.
+Remove the rules with the given UIDs\&.
 .RE
 .PP
-\fBrestore_layout\fR \fIFILE_PATH\fR
+\fB\-l\fR, \fB\-\-list\fR [<pattern>]
 .RS 4
-Restore the layout of each desktop from the content of
-\fIFILE_PATH\fR\&.
+List the rules\&.
+.RE
 .RE
+.SS "Config"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
+.RS 4
 .PP
-\fBrestore_history\fR \fIFILE_PATH\fR
+config <key> [<value>]
 .RS 4
-Restore the history of each desktop from the content of
-\fIFILE_PATH\fR\&.
+Get or set the value of <key>\&.
+.RE
 .RE
+.SS "Quit"
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBGeneral Syntax\fR
+.RS 4
 .PP
-\fBquit\fR [\fIEXIT_STATUS\fR]
+quit [<status>]
 .RS 4
-Quit\&.
+Quit with an optional exit status\&.
+.RE
 .RE
 .SH "SETTINGS"
 .sp
@@ -645,7 +787,7 @@ works)\&.
 .sp -1
 .IP \(bu 2.3
 .\}
-Automatic and manual modes\&.
+Hybrid tiling\&.
 .RE
 .SH "CONTRIBUTORS"
 .sp
index 65fe9b4b35f99d06e440fc93dd69188965c72b3b..278cac1cf017f1156cb4b68f12ce183ed0b392c0 100644 (file)
@@ -15,7 +15,7 @@ Synopsis
 
 *bspwm* [*-h*|*-v*|*-s* 'PANEL_FIFO'|*-p* 'PANEL_PREFIX']
 
-*bspc* 'MESSAGE' ['ARGUMENTS'] ['OPTIONS']
+*bspc* 'COMMAND' ['ARGUMENTS']
 
 Description
 -----------
@@ -24,6 +24,23 @@ Description
 
 It is controlled and configured via *bspc*.
 
+
+Options
+-------
+
+*-h*::
+    Print the synopsis and exit.
+
+*-v*::
+    Print the version and exit.
+
+*-s* 'PANEL_FIFO'::
+    Write the internal state to the given FIFO.
+
+*-p* 'PANEL_PREFIX'::
+    Start every line written to the 'PANEL_FIFO' with the given prefix.
+
+
 Configuration
 -------------
 
@@ -38,9 +55,11 @@ Example configuration files can be found in the *examples* directory.
 Splitting Modes
 ---------------
 
+New windows are inserted in the tree as close as possible to the focused window.
+
 There is only two splitting modes: 'automatic' and 'manual'.
 
-The default mode is 'automatic'. The 'manual' mode is entered by sending a *presel* message.
+The default mode is 'automatic'. The 'manual' mode is entered by sending a *preselection* message.
 
 Example: insertion of a new node (number 4) into the given tree in 'automatic' mode:
 
@@ -63,7 +82,7 @@ Example: insertion of a new node (number 4) into the given tree in 'automatic' m
  +-------------------------+         +-------------------------+
 ----
 
-Same departure, but the mode is 'manual', and a *presel* 'up' message was sent beforehand:
+Same departure, but the mode is 'manual', and a *window --presel up* message was sent beforehand:
 
 ----
              b                                   b
@@ -90,189 +109,254 @@ Each monitor contains at least one desktop.
 
 Each desktop contains at most one tree.
 
-Messages
+
+Definitions
+-----------
+
+----
+WINDOW_SEL     :=  (CYCLE|DIR|biggest|focused|last)[.WINDOW_CLASS] | <window_id>
+DESKTOP_SEL    :=  (CYCLE|focused|last)[.DESKTOP_CLASS] | <desktop_name>
+MONITOR_SEL    :=  (CYCLE|DIR|focused|last)[.DESKTOP_CLASS] | <monitor_name>
+
+DESKTOP_CLASS  :=  [occupied|free]
+WINDOW_CLASS   :=  [floating|tiled][.][like|unlike]
+
+DIR            :=  left|right|up|down
+CYCLE          :=  next|prev
+ROTATE         :=  90|270|180
+FLIP           :=  horizontal|vertical
+----
+
+Commands
 --------
 
+Window
+~~~~~~
 
-*get* 'SETTING'::
-    Return the value of the given setting.
+General Syntax
+^^^^^^^^^^^^^^
 
-*set* 'SETTING' 'VALUE'::
-    Set the value of the given setting.
+window ['WINDOW_SEL'] 'OPTIONS'
 
-*list* ['DESKTOP_NAME']::
-    Output the internal representation of the window tree.
+Options
+^^^^^^^
+*-f*, *--focus* ['WINDOW_SEL']::
+    Focus the selected or given window.
 
-*list_desktops* [*--quiet*]::
-    Perform a dump of each desktop for the current monitor.
+*-d*, *--to-desktop* 'DESKTOP_SEL'::
+    Send the selected window to the given desktop.
 
-*list_monitors* [*--quiet*]::
-    Perform a dump of each monitor.
+*-m*, *--to-monitor* 'MONITOR_SEL'::
+    Send the selected window to the given monitor.
 
-*list_history*::
-    Return the node focus history of each desktop.
+*-w*, *--to-window* 'WINDOW_SEL'::
+    Transplant the selected window to the given window.
 
-*list_windows*::
-    Return the list of managed windows (i.e. their identifiers).
+*-s*, *--swap* 'WINDOW_SEL'::
+    Swap the selected window with the given window.
 
-*list_rules*::
-    Return the list of rules.
+*-p*, *--presel* 'DIR'|cancel::
+    Preselect the splitting area of the selected window (or cancel the preselection).
 
-*presel* 'left'|'right'|'up'|'down' ['SPLIT_RATIO']::
-    Switch to manual mode and select the splitting direction.
+*-r*, *--ratio* 'RATIO'::
+    Set the splitting ratio of the selected window.
 
-*cancel* [*--all*]::
-    Switch to automatic mode.
+*-e*, *--edge* 'DIR' 'RATIO'|pull|push::
+    Set the splitting ratio (or pull, or push) the edge located in the given direction in relation to the selected window.
 
-*ratio* 'VALUE'::
-    Set the splitting ratio of the focused window.
+*-t*, *--toggle* floating|fullscreen|locked[=on|off]::
+    Set or toggle the given state for the selected window.
 
-*pad* 'MONITOR_NAME' ['TOP_PADDING' ['RIGHT_PADDING' ['BOTTOM_PADDING' ['LEFT_PADDING']]]]::
-    Set the padding of the given monitor.
+*-c*, *--close*::
+    Close the selected window.
 
-*focus* 'left'|'right'|'up'|'down'::
-    Focus the neighbor window situated in the given direction.
+*-k*, *--kill*::
+    Kill the selected window.
 
-*shift* 'left'|'right'|'up'|'down'::
-    Exchange the current window with the given neighbor.
+Desktop
+~~~~~~~
 
-*swap* [*--keep-focus*]::
-    Swap the focused window with the last focused window.
+General Syntax
+^^^^^^^^^^^^^^
 
-*push* 'left'|'right'|'up'|'down'::
-    Push the fence located in the given direction.
+desktop ['DESKTOP_SEL'] 'OPTIONS'
 
-*pull* 'left'|'right'|'up'|'down'::
-    Pull the fence located in the given direction.
+Options
+^^^^^^^
+*-f*, *--focus* ['DESKTOP_SEL']::
+    Focus the selected or given desktop.
 
-*fence_ratio* 'left'|'right'|'up'|'down' 'RATIO'::
-    Set the splitting ratio of the fence located in the given direction.
+*-m*, *--to-monitor* 'MONITOR_SEL'::
+    Send the selected desktop to the given monitor.
 
-*cycle* 'next'|'prev' [*--skip-floating*|*--skip-tiled*|*--skip-class-equal*|*--skip-class-differ*]::
-    Focus the next or previous window matching the given constraints.
+*-l*, *--layout* 'CYCLE'|monocle|tiled::
+    Set or cycle the layout of the selected desktop.
 
-*nearest* 'older'|'newer' [*--skip-floating*|*--skip-tiled*|*--skip-class-equal*|*--skip-class-differ*]::
-    Focus the nearest window matching the given constraints.
+*-n*, *--rename* <new_name>::
+    Rename the selected desktop.
 
-*biggest*::
-    Return the ID of the biggest tiled window.
+*-r*, *--remove*::
+    Remove the selected desktop.
 
-*circulate* 'forward'|'backward'::
-    Circulate the leaves in the given direction.
+*-c*, *--cancel-presel*::
+    Cancel the preselection of all the windows of the selected desktop.
 
-*grab_pointer* 'focus'|'move'|'resize_side'|'resize_corner'::
-    Begin the specified pointer action.
+*-F*, *--flip* 'FLIP'::
+    Flip the tree of the selected desktop.
 
-*track_pointer* 'ROOT_X' 'ROOT_Y'::
-    Pass the pointer root coordinates for the current pointer action.
+*-R*, *--rotate* 'ROTATE'::
+    Rotate the tree of the selected desktop.
 
-*toggle_fullscreen*::
-    Toggle the fullscreen state of the current window.
+*-B*, *--balance*::
+    Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
 
-*toggle_floating*::
-    Toggle the floating state of the current window.
+*-C*, *--circulate* forward|backward::
+    Circulate the leaves of the tree of the selected desktop.
 
-*toggle_locked*::
-    Toggle the locked state of the current window (locked windows will not respond to the *close* message).
+Monitor
+~~~~~~~
 
-*toggle_visibility*::
-    Toggle the visibility of all the managed windows.
+General Syntax
+^^^^^^^^^^^^^^
 
-*close*::
-    Close the focused window.
+monitor ['MONITOR_SEL'] 'OPTIONS'
 
-*kill*::
-    Kill the focused window.
+Options
+^^^^^^^
+*-f*, *--focus* ['MONITOR_SEL']::
+    Focus the selected or given monitor.
 
-*send_to* 'DESKTOP_NAME' [*--follow*]::
-    Send the focused window to the given desktop.
+*-a*, *--add-desktops* <name>...::
+    Create desktops with the given names in the selected monitor.
 
-*drop_to* 'next'|'prev' [*--follow*]::
-    Send the focused window to the next or previous desktop.
+*-r*, *--remove-desktops* <name>...::
+    Remove desktops with the given names.
 
-*send_to_monitor* 'MONITOR_NAME' [*--follow*]::
-    Send the focused window to the given monitor.
+*-p*, *--pad* <top> <right> <bottom> <left>::
+    Set the padding of the selected monitor.
 
-*drop_to_monitor* 'next'|'prev' [*--follow*]::
-    Send the focused window to the next or previous monitor.
+*-n*, *--rename* <new_name>::
+    Rename the selected monitor.
 
-*use* 'DESKTOP_NAME'::
-    Select the given desktop.
+Query
+~~~~~
 
-*use_monitor* 'MONITOR_NAME'::
-    Select the given monitor.
+General Syntax
+^^^^^^^^^^^^^^
 
-*focus_monitor* 'left'|'right'|'up'|'down'::
-    Focus the nearest monitor in the given direction.
+query 'OPTIONS'
 
-*alternate*::
-    Alternate between the current and the last focused window.
+Options
+^^^^^^^
+*-W*, *--windows*::
+    List matching windows.
 
-*alternate_desktop*::
-    Alternate between the current and the last focused desktop.
+*-D*, *--desktops*::
+    List matching desktops.
 
-*alternate_monitor*::
-    Alternate between the current and the last focused monitor.
+*-M*, *--monitors*::
+    List matching monitors.
 
-*add* 'DESKTOP_NAME' ...::
-    Make new desktops with the given names.
+*-T*, *--tree*::
+    Print tree rooted at query.
 
-*add_in* 'MONITOR_NAME' 'DESKTOP_NAME' ...::
-    Make new desktops with the given names in the given monitor.
+*-H*, *--history*::
+    Print the history as it relates to the query.
 
-*rename_monitor* 'CURRENT_NAME' 'NEW_NAME'::
-    Rename the monitor named 'CURRENT_NAME' to 'NEW_NAME'.
+[*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]::
+    Constrain matches to the selected monitor, desktop or window.
 
-*rename* 'CURRENT_NAME' 'NEW_NAME'::
-    Rename the desktop named 'CURRENT_NAME' to 'NEW_NAME'.
+Restore
+~~~~~~~
 
-*remove_desktop* 'DESKTOP_NAME' ...::
-    Remove the given desktops.
+General Syntax
+^^^^^^^^^^^^^^
 
-*send_desktop_to* 'MONITOR_NAME' [*--follow*]::
-    Send the current desktop to the given monitor.
+restore 'OPTIONS'
+
+Options
+^^^^^^^
+
+*-T*, *--tree* <file_path>::
+    Load the desktop trees from the given file.
+
+*-H*, *--history* <file_path>::
+    Load the focus history from the given file.
+
+Control
+~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+
+control 'OPTIONS'
+
+Options
+^^^^^^^
+
+*--adopt-orphans* <file_path>::
+   Manage all the unmanaged windows remaining from a previous session.
+
+*--put-status* <file_path>::
+    Write the current internal state to the panel FIFO.
+
+*--toggle-visibility*::
+    Toggle the visibility of all the managed windows.
+
+Pointer
+~~~~~~~
+
+General Syntax
+^^^^^^^^^^^^^^
+
+pointer 'OPTIONS'
+
+Options
+^^^^^^^
+
+*-t*, *--track* <x> <y>::
+    Pass the pointer root coordinates for the current pointer action.
 
-*cycle_monitor* 'next'|'prev'::
-    Select the next or previous monitor.
+*-g*, *--grab* focus|move|resize_side|resize_corner::
+    Perform the given pointer action.
 
-*cycle_desktop* 'next'|'prev' [*--skip-free*|*--skip-occupied*]::
-    Select the next or previous desktop.
+Rule
+~~~~
 
-*layout* 'monocle'|'tiled' ['DESKTOP_NAME' ...]::
-    Set the layout of the given desktops (current if none given).
+General Syntax
+^^^^^^^^^^^^^^
 
-*cycle_layout*::
-    Cycle the layout of the current desktop.
+rule 'OPTIONS'
 
-*rotate* 'clockwise'|'counter_clockwise'|'full_cycle'::
-    Rotate the window tree.
+Options
+^^^^^^^
 
-*flip* 'horizontal'|'vertical'::
-    Flip the window tree.
+*-a*, *--add* <pattern> [-d 'DESKTOP_SEL'] [--floating] [--follow]::
+    Create a new rule (<pattern> must match the class or instance name).
 
-*balance*::
-    Adjust the split ratios so that all windows occupy the same area.
+*-r*, *--rm* <rule_uid>...::
+    Remove the rules with the given UIDs.
 
-*rule* 'PATTERN' ['DESKTOP_NAME'] ['floating'] ['follow']::
-    Create a new rule ('PATTERN' must match the class or instance name).
+*-l*, *--list* [<pattern>]::
+    List the rules.
 
-*remove_rule* 'UID' ...::
-    Remove the rules with the given 'UIDs'.
+Config
+~~~~~~
 
-*put_status*::
-    Output the current state to the panel fifo.
+General Syntax
+^^^^^^^^^^^^^^
 
-*adopt_orphans*::
-    Manage all the unmanaged windows remaining from a previous session.
+config <key> [<value>]::
+    Get or set the value of <key>.
 
-*restore_layout* 'FILE_PATH'::
-    Restore the layout of each desktop from the content of 'FILE_PATH'.
+Quit
+~~~~
 
-*restore_history* 'FILE_PATH'::
-    Restore the history of each desktop from the content of 'FILE_PATH'.
+General Syntax
+^^^^^^^^^^^^^^
 
-*quit* ['EXIT_STATUS']::
-    Quit.
+quit [<status>]::
+    Quit with an optional exit status.
 
 Settings
 --------
@@ -374,7 +458,7 @@ Key Features
 * Configured and controlled through messages.
 * Multiple monitors support (via 'RandR').
 * EWMH support (*tint2* works).
-* Automatic and manual modes.
+* Hybrid tiling.
 
 Contributors
 ------------
index 35372dfcb45563458d232e97ffd37d02c63a2589..2801c4b10fdb9d86f6fecef02d2f1b557c960af3 100644 (file)
--- a/events.c
+++ b/events.c
@@ -12,6 +12,7 @@
 #include "window.h"
 #include "events.h"
 #include "tree.h"
+#include "query.h"
 #include "rules.h"
 #include "ewmh.h"
 
@@ -65,7 +66,7 @@ void configure_request(xcb_generic_event_t *evt)
 
     PRINTF("configure request %X\n", e->window);
 
-    window_location_t loc;
+    coordinates_t loc;
     bool is_managed = locate_window(e->window, &loc);
 
     if (!is_managed || is_floating(loc.node->client)) {
@@ -154,7 +155,7 @@ void destroy_notify(xcb_generic_event_t *evt)
 
     PRINTF("destroy notify %X\n", e->window);
 
-    window_location_t loc;
+    coordinates_t loc;
     if (locate_window(e->window, &loc)) {
         remove_node(loc.desktop, loc.node);
         arrange(loc.monitor, loc.desktop);
@@ -167,7 +168,7 @@ void unmap_notify(xcb_generic_event_t *evt)
 
     PRINTF("unmap notify %X\n", e->window);
 
-    window_location_t loc;
+    coordinates_t loc;
     if (locate_window(e->window, &loc)) {
         remove_node(loc.desktop, loc.node);
         arrange(loc.monitor, loc.desktop);
@@ -184,7 +185,7 @@ void property_notify(xcb_generic_event_t *evt)
     if (e->atom != XCB_ATOM_WM_HINTS)
         return;
 
-    window_location_t loc;
+    coordinates_t loc;
     if (locate_window(e->window, &loc)
             && xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1)
         set_urgency(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
@@ -197,13 +198,13 @@ void client_message(xcb_generic_event_t *evt)
     PRINTF("client message %X %u\n", e->window, e->type);
 
     if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
-        desktop_location_t loc;
+        coordinates_t loc;
         if (ewmh_locate_desktop(e->data.data32[0], &loc))
             focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
         return;
     }
 
-    window_location_t loc;
+    coordinates_t loc;
     if (!locate_window(e->window, &loc))
         return;
 
@@ -214,12 +215,12 @@ void client_message(xcb_generic_event_t *evt)
         if (loc.node == mon->desk->focus)
             return;
         if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node) {
-            toggle_fullscreen(loc.desktop, loc.desktop->focus);
+            set_fullscreen(loc.desktop, loc.desktop->focus, false);
             arrange(loc.monitor, loc.desktop);
         }
         focus_node(loc.monitor, loc.desktop, loc.node);
     } else if (e->type == ewmh->_NET_WM_DESKTOP) {
-        desktop_location_t dloc;
+        coordinates_t dloc;
         if (ewmh_locate_desktop(e->data.data32[0], &dloc))
             transfer_node(loc.monitor, loc.desktop, dloc.monitor, dloc.desktop, loc.node);
     }
@@ -258,13 +259,13 @@ void motion_notify(void)
 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
 {
     if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
-        bool fs = n->client->fullscreen;
-        if (action == XCB_EWMH_WM_STATE_TOGGLE
-                || (fs && action == XCB_EWMH_WM_STATE_REMOVE)
-                || (!fs && action == XCB_EWMH_WM_STATE_ADD)) {
-            toggle_fullscreen(d, n);
-            arrange(m, d);
-        }
+        if (action == XCB_EWMH_WM_STATE_ADD)
+            set_fullscreen(d, n, true);
+        else if (action == XCB_EWMH_WM_STATE_REMOVE)
+            set_fullscreen(d, n, false);
+        else if (action == XCB_EWMH_WM_STATE_TOGGLE)
+            set_fullscreen(d, n, !n->client->fullscreen);
+        arrange(m, d);
     } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
         if (action == XCB_EWMH_WM_STATE_ADD)
             set_urgency(m, d, n, true);
@@ -287,7 +288,7 @@ void grab_pointer(pointer_action_t pac)
     if (win == XCB_NONE)
         return;
 
-    window_location_t loc;
+    coordinates_t loc;
     if (locate_window(win, &loc)) {
         client_t *c = NULL;
         frozen_pointer->position = pos;
@@ -438,7 +439,7 @@ void track_pointer(int root_x, int root_y)
                 query_pointer(&pwin, NULL);
                 if (pwin == win)
                     return;
-                window_location_t loc;
+                coordinates_t loc;
                 bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
                 if (is_managed && is_tiled(loc.node->client) && loc.monitor == m) {
                     swap_nodes(n, loc.node, true);
diff --git a/ewmh.c b/ewmh.c
index d18ad385a26590a99fb6e6fbd25fd65e793ca273..f41fb75e759c035b85550ef391861b769e96503b 100644 (file)
--- a/ewmh.c
+++ b/ewmh.c
@@ -42,16 +42,16 @@ uint32_t ewmh_get_desktop_index(desktop_t *d)
     return 0;
 }
 
-bool ewmh_locate_desktop(uint32_t i, desktop_location_t *loc)
+bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
 {
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--)
             if (i == 0) {
                 loc->monitor = m;
                 loc->desktop = d;
+                loc->node = NULL;
                 return true;
             }
-
     return false;
 }
 
diff --git a/ewmh.h b/ewmh.h
index e3e9e7e3404c74590a4618f84bf016eb9cde05cf..ad10884a60d9a6632dac200487d6f5158fd4f9cf 100644 (file)
--- a/ewmh.h
+++ b/ewmh.h
@@ -10,7 +10,7 @@ void ewmh_update_wm_name(void);
 void ewmh_update_active_window(void);
 void ewmh_update_number_of_desktops(void);
 uint32_t ewmh_get_desktop_index(desktop_t *);
-bool ewmh_locate_desktop(uint32_t, desktop_location_t *);
+bool ewmh_locate_desktop(uint32_t, coordinates_t *);
 void ewmh_update_current_desktop(void);
 void ewmh_set_wm_desktop(node_t *, desktop_t *);
 void ewmh_update_desktop_names(void);
index 78c7eec1d7aa01b3685394425302ce5003813630..67a57362b778d345e09def0f261cc47254a82023 100755 (executable)
@@ -2,14 +2,14 @@
 
 FIRST_DESK=1
 REMAINING_DESKS="$(seq 2 9) 0"
-bspc rename Desktop01 $FIRST_DESK
-bspc add $REMAINING_DESKS
+bspc desktop Desktop01 -n $FIRST_DESK
+bspc monitor -a $REMAINING_DESKS
 
-bspc rule Gimp Eight floating
+bspc rule -a Gimp -d Eight --floating
 
-bspc set split_ratio         0.52
-bspc set border_width        2
-bspc set window_gap          12
-bspc set borderless_monocle  true
-bspc set gapless_monocle     true
-bspc set focus_by_distance   true
+bspc config split_ratio         0.52
+bspc config border_width        2
+bspc config window_gap          12
+bspc config borderless_monocle  true
+bspc config gapless_monocle     true
+bspc config focus_by_distance   true
index dad05c881d07a56cb00ac224dde64fbfe2df25a4..4d27e3233932e6c57688a896bccffa760ae9d50e 100644 (file)
@@ -6,67 +6,64 @@ super + alt + Escape
     bspc quit
 
 super + w
-    bspc close
+    bspc window -c
 
 super + t
-    bspc cycle_layout
+    bspc desktop -l next
 
 super + b
-    bspc balance
+    bspc desktop -B
 
 super + {s,f}
-    bspc toggle_{floating,fullscreen}
+    bspc window -t {floating,fullscreen}
 
 super + {grave,Tab}
-    bspc {alternate,alternate_desktop}
+    bspc {window,desktop} -f last
 
 super + apostrophe
-    bspc swap
+    bspc window -s last
 
 super + m
-    cur=$(xdo id); \
-    wid=$(bspc biggest); \
-    [ -n "$wid" -a "$cur" != "$wid" ] && \
-    xdo activate $wid && bspc swap
+    bspc window -s biggest
 
 super + {_,shift + }{h,j,k,l}
-    bspc {focus,shift} {left,down,up,right}
+    bspc window -{f,s} {left,down,up,right}
 
-super + {c,shift + c}
-    bspc cycle {next,prev}
+super + {_,shift + }c
+    bspc window -f {next,prev}
 
 super + {comma,period}
-    bspc circulate {backward,forward}
+    bspc desktop -C {backward,forward}
 
 super + bracket{left,right}
-    bspc cycle_desktop {prev,next}
+    bspc desktop -f {prev,next}
 
 super + ctrl + {h,j,k,l}
-    bspc presel {left,down,up,right}
+    bspc window -p {left,down,up,right}
 
 super + ctrl + {_,shift + }space
-    bspc cancel{_, --all }
+    bspc {window -p cancel,desktop -c}
 
 super + alt + {h,j,k,l}
-    bspc push {left,down,up,right}
+    bspc window -e {left,down,up,right} push
 
 super + alt + shift + {h,j,k,l}
-    bspc pull {right,up,down,left}
+    bspc window -e {right,up,down,left} pull
 
 super + ctrl + {1-9}
-    bspc ratio 0.{1-9}
+    bspc window -r 0.{1-9}
 
 super + {_,shift + }{1-9,0}
-    bspc {use,send_to} {1-9,0}
+    bspc {desktop -f,window -d} {1-9,0}
 
 :button1
-    bspc grab_pointer focus
+    bspc pointer -g focus
 
 super + button{1-3}
-    bspc grab_pointer {move,resize_side,resize_corner}
+    bspc pointer -g {move,resize_side,resize_corner}
 
 super + !button{1-3}
-    bspc track_pointer %i %i
+    bspc pointer -t %i %i
 
 #
 # wm independent hotkeys
index 45e3e6a11c8c4cea4ae22d2187720d9c243d3882..f538462d8a4d32f42d4bd80b73fdbf6f0d374379 100644 (file)
--- a/helpers.c
+++ b/helpers.c
@@ -24,10 +24,9 @@ void err(char *fmt, ...)
     exit(EXIT_FAILURE);
 }
 
-uint32_t get_color(char *col)
+bool get_color(char *col, uint32_t *pxl)
 {
     xcb_colormap_t map = screen->default_colormap;
-    uint32_t pxl = 0;
 
     if (col[0] == '#') {
         unsigned int red, green, blue;
@@ -38,19 +37,20 @@ uint32_t get_color(char *col)
             blue *= 0x101;
             xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(dpy, xcb_alloc_color(dpy, map, red, green, blue), NULL);
             if (reply != NULL) {
-                pxl = reply->pixel;
+                *pxl = reply->pixel;
                 free(reply);
+                return true;
             }
         }
     } else {
         xcb_alloc_named_color_reply_t *reply = xcb_alloc_named_color_reply(dpy, xcb_alloc_named_color(dpy, map, strlen(col), col), NULL);
         if (reply != NULL) {
-            pxl = reply->pixel;
+            *pxl = reply->pixel;
             free(reply);
+            return true;
         }
     }
-
-    return pxl;
+    return false;
 }
 
 double distance(xcb_point_t a, xcb_point_t b)
index 9be286aff5e675e35b8395a0147678890c858553..ff037614e586c5e405b28bb4d1b675768ae1dedc 100644 (file)
--- a/helpers.h
+++ b/helpers.h
@@ -4,6 +4,7 @@
 #include <xcb/xcb.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <stdint.h>
 
 #define LENGTH(x)         (sizeof(x) / sizeof(*x))
 #define XCB_CONFIG_WINDOW_X_Y               XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
 #define XCB_CONFIG_WINDOW_WIDTH_HEIGHT      XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT
 #define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT  XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT
-#define MAXLEN 256
+
+#define MAXLEN    256
+#define INIT_CAP    8
 
 #define REMLEN(x)         (BUFSIZ - strlen(x) - 1)
+#define streq(s1, s2)     (strcmp((s1), (s2)) == 0)
 
 #ifdef DEBUG
 #  define PUTS(x)         puts(x)
@@ -30,7 +34,7 @@
 void warn(char *, ...);
 __attribute__((noreturn))
 void err(char *, ...);
-uint32_t get_color(char *);
+bool get_color(char *, uint32_t *);
 double distance(xcb_point_t, xcb_point_t);
 
 #endif
index 3109605dbf67685adcf382d6383b524dd268e0a9..8a34f1829b126246f06b52fb62c1ab6d6202283b 100644 (file)
@@ -1,8 +1,11 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
 #include "settings.h"
 #include "messages.h"
+#include "query.h"
+#include "restore.h"
 #include "common.h"
 #include "types.h"
 #include "bspwm.h"
 #include "tree.h"
 #include "rules.h"
 
-void process_message(char *msg, char *rsp)
+bool cmd_window(char **args, int num)
 {
-    char *cmd = strtok(msg, TOK_SEP);
-
-    if (cmd == NULL)
-        return;
-
-    if (strcmp(cmd, "get") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        get_setting(name, rsp);
-    } else if (strcmp(cmd, "set") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        char *value = strtok(NULL, TOK_SEP);
-        set_setting(name, value, rsp);
-    } else if (strcmp(cmd, "list") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            desktop_location_t loc;
-            if (locate_desktop(name, &loc))
-                list(loc.desktop, loc.desktop->root, rsp, 0);
-        } else {
-            list(mon->desk, mon->desk->root, rsp, 0);
-        }
-    } else if (strcmp(cmd, "list_monitors") == 0) {
-        char *arg = strtok(NULL, TOK_SEP);
-        list_option_t opt;
-        if (parse_list_option(arg, &opt))
-            list_monitors(opt, rsp);
-    } else if (strcmp(cmd, "list_desktops") == 0) {
-        char *arg = strtok(NULL, TOK_SEP);
-        list_option_t opt;
-        if (parse_list_option(arg, &opt))
-            list_desktops(mon, opt, 0, rsp);
-    } else if (strcmp(cmd, "list_windows") == 0) {
-        list_windows(rsp);
-    } else if (strcmp(cmd, "list_history") == 0) {
-        list_history(rsp);
-    } else if (strcmp(cmd, "list_rules") == 0) {
-        list_rules(rsp);
-    } else if (strcmp(cmd, "close") == 0) {
-        window_close(mon->desk->focus);
-    } else if (strcmp(cmd, "kill") == 0) {
-        window_kill(mon, mon->desk, mon->desk->focus);
-    } else if (strcmp(cmd, "rotate") == 0) {
-        char *deg = strtok(NULL, TOK_SEP);
-        if (deg != NULL) {
-            rotate_t r;
-            if (parse_rotate(deg, &r))
-                rotate_tree(mon->desk->root, r);
-        }
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "flip") == 0) {
-        char *flp = strtok(NULL, TOK_SEP);
-        if (flp != NULL) {
-            flip_t f;
-            if (parse_flip(flp, &f))
-                flip_tree(mon->desk->root, f);
-        }
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "balance") == 0) {
-        balance_tree(mon->desk->root);
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "grab_pointer") == 0) {
-        char *pac = strtok(NULL, TOK_SEP);
-        if (pac != NULL) {
-            pointer_action_t a;
-            if (parse_pointer_action(pac, &a))
-                grab_pointer(a);
-        }
-    } else if (strcmp(cmd, "track_pointer") == 0) {
-        char *arg1 = strtok(NULL, TOK_SEP);
-        char *arg2 = strtok(NULL, TOK_SEP);
-        if (arg1 == NULL || arg2 == NULL)
-            return;
-        int root_x, root_y;
-        if (sscanf(arg1, "%i", &root_x) == 1 && sscanf(arg2, "%i", &root_y) == 1)
-            track_pointer(root_x, root_y);
-    } else if (strcmp(cmd, "layout") == 0) {
-        char *lyt = strtok(NULL, TOK_SEP);
-        if (lyt != NULL) {
-            layout_t l;
-            if (parse_layout(lyt, &l)) {
-                char *name = strtok(NULL, TOK_SEP);
-                if (name == NULL) {
-                    change_layout(mon, mon->desk, l);
-                } else {
-                    desktop_location_t loc;
-                    do {
-                        if (locate_desktop(name, &loc))
-                            change_layout(loc.monitor, loc.desktop, l);
-                    } while ((name = strtok(NULL, TOK_SEP)) != NULL);
-                }
-            }
-        }
-    } else if (strcmp(cmd, "cycle_layout") == 0) {
-        if (mon->desk->layout == LAYOUT_MONOCLE)
-            change_layout(mon, mon->desk, LAYOUT_TILED);
+    coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+    coordinates_t trg = ref;
+
+    if (*args[0] != OPT_CHR) {
+        if (node_from_desc(*args, &ref, &trg))
+            num--, args++;
         else
-            change_layout(mon, mon->desk, LAYOUT_MONOCLE);
-    } else if (strcmp(cmd, "shift") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            direction_t d;
-            if (parse_direction(dir, &d)) {
-                node_t *n = nearest_neighbor(mon->desk, mon->desk->focus, d);
-                if (n != NULL) {
-                    swap_nodes(mon->desk->focus, n, true);
-                    arrange(mon, mon->desk);
-                } else if (monitor_focus_fallback) {
-                    monitor_t *m = nearest_monitor(d);
-                    if (m != NULL) {
-                        transfer_node(mon, mon->desk, m, m->desk, mon->desk->focus);
-                        focus_node(m, m->desk, m->desk->focus);
-                    }
-                }
+            return false;
+    }
+
+    if (trg.node == NULL)
+        return false;
+
+    bool dirty = false;
+
+    while (num > 0) {
+        if (streq("-f", *args) || streq("--focus", *args)) {
+            coordinates_t dst = trg;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!node_from_desc(*args, &trg, &dst))
+                    return false;
             }
-        }
-    } else if (strcmp(cmd, "toggle_fullscreen") == 0) {
-        toggle_fullscreen(mon->desk, mon->desk->focus);
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "toggle_floating") == 0) {
-        toggle_floating(mon->desk, mon->desk->focus);
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "toggle_locked") == 0) {
-        toggle_locked(mon, mon->desk, mon->desk->focus);
-    } else if (strcmp(cmd, "toggle_visibility") == 0) {
-        toggle_visibility();
-    } else if (strcmp(cmd, "pad") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            monitor_t *m = find_monitor(name);
-            if (m != NULL) {
-                char args[BUFSIZ] = {0}, *s;
-                while ((s = strtok(NULL, TOK_SEP)) != NULL) {
-                    strncat(args, s, REMLEN(args));
-                    strncat(args, TOK_SEP, REMLEN(args));
-                }
-                if (strlen(args) > 0) {
-                    sscanf(args, "%i %i %i %i", &m->top_padding, &m->right_padding, &m->bottom_padding, &m->left_padding);
-                    arrange(m, m->desk);
-                } else {
-                    snprintf(rsp, BUFSIZ, "%i %i %i %i\n", m->top_padding, m->right_padding, m->bottom_padding, m->left_padding);
-                }
+            focus_node(dst.monitor, dst.desktop, dst.node);
+        } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
+            num--, args++;
+            coordinates_t dst;
+            if (desktop_from_desc(*args, &trg, &dst)) {
+                transfer_node(trg.monitor, trg.desktop, dst.monitor, dst.desktop, trg.node);
+                trg.monitor = dst.monitor;
+                trg.desktop = dst.desktop;
+            } else {
+                return false;
             }
-        }
-    } else if (strcmp(cmd, "ratio") == 0) {
-        char *value;
-        if (mon->desk->focus != NULL && (value = strtok(NULL, TOK_SEP)) != NULL &&
-                sscanf(value, "%lf", &mon->desk->focus->split_ratio) == 1)
-            window_draw_border(mon->desk->focus, true, true);
-    } else if (strcmp(cmd, "cancel") == 0) {
-        if (mon->desk->focus == NULL)
-            return;
-        char *opt = strtok(NULL, TOK_SEP);
-        cancel_option_t o;
-        if (parse_cancel_option(opt, &o))
-            reset_mode(mon->desk, mon->desk->focus, o);
-    } else if (strcmp(cmd, "presel") == 0) {
-        if (mon->desk->focus == NULL || !is_tiled(mon->desk->focus->client) || mon->desk->layout != LAYOUT_TILED)
-            return;
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            direction_t d;
-            if (parse_direction(dir, &d)) {
-                char *rat = strtok(NULL, TOK_SEP);
-                double r = mon->desk->focus->split_ratio;
-                if (rat != NULL)
-                    sscanf(rat, "%lf", &r);
-                if (auto_cancel && mon->desk->focus->split_mode == MODE_MANUAL
-                        && d == mon->desk->focus->split_dir
-                        && r == mon->desk->focus->split_ratio) {
-                    reset_mode(mon->desk, mon->desk->focus, CANCEL_OPTION_FOCUSED);
+        } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            coordinates_t dst;
+            if (monitor_from_desc(*args, &trg, &dst)) {
+                transfer_node(trg.monitor, trg.desktop, dst.monitor, dst.monitor->desk, trg.node);
+                trg.monitor = dst.monitor;
+                trg.desktop = dst.monitor->desk;
+            } else {
+                return false;
+            }
+        } else if (streq("-w", *args) || streq("--to-window", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            coordinates_t dst;
+            if (node_from_desc(*args, &trg, &dst))
+                transplant_node(trg.monitor, trg.desktop, trg.node, dst.node);
+            else
+                return false;
+            dirty = true;
+        } else if (streq("-s", *args) || streq("--swap", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            coordinates_t dst;
+            if (node_from_desc(*args, &trg, &dst))
+                swap_nodes(trg.node, dst.node, true);
+            else
+                return false;
+            dirty = true;
+        } else if (streq("-t", *args) || streq("--toggle", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            char *key = strtok(*args, EQL_TOK);
+            char *val = strtok(NULL, EQL_TOK);
+            state_alter_t a = ALTER_NONE;
+            bool b;
+            if (val == NULL) {
+                a = ALTER_TOGGLE;
+            } else {
+                if (parse_bool(val, &b))
+                    a = ALTER_SET;
+                else
+                    return false;
+            }
+            if (streq("fullscreen", key)) {
+                set_fullscreen(trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->fullscreen));
+                dirty = true;
+            } else if (streq("floating", key)) {
+                set_floating(trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->floating));
+                dirty = true;
+            } else if (streq("locked", key)) {
+                set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
+            }
+        } else if (streq("-p", *args) || streq("--presel", *args)) {
+            num--, args++;
+            if (num < 1 || !is_tiled(trg.node->client)
+                    || trg.desktop->layout != LAYOUT_TILED)
+                return false;
+            if (streq("cancel", *args)) {
+                reset_mode(&trg);
+            } else {
+                direction_t dir;
+                if (parse_direction(*args, &dir)) {
+                    double rat = trg.node->split_ratio;
+                    if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                        num--, args++;
+                        if (sscanf(*args, "%lf", &rat) != 1)
+                            return false;
+                    }
+                    if (auto_cancel && trg.node->split_mode == MODE_MANUAL
+                            && dir == trg.node->split_dir
+                            && rat == trg.node->split_ratio) {
+                        reset_mode(&trg);
+                    } else {
+                        trg.node->split_mode = MODE_MANUAL;
+                        trg.node->split_dir = dir;
+                        trg.node->split_ratio = rat;
+                    }
+                    window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
                 } else {
-                    mon->desk->focus->split_mode = MODE_MANUAL;
-                    mon->desk->focus->split_dir = d;
-                    mon->desk->focus->split_ratio = r;
+                    return false;
                 }
-                window_draw_border(mon->desk->focus, true, true);
             }
-        }
-    } else if (strcmp(cmd, "push") == 0 || strcmp(cmd, "pull") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            fence_move_t m;
-            direction_t d;
-            if (parse_fence_move(cmd, &m) && parse_direction(dir, &d)) {
-                move_fence(mon->desk->focus, d, m);
-                arrange(mon, mon->desk);
-            }
-        }
-    } else if (strcmp(cmd, "fence_ratio") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            direction_t d;
-            node_t *n;
-            if (parse_direction(dir, &d) && (n = find_fence(mon->desk->focus, d)) != NULL) {
-                char *value = strtok(NULL, TOK_SEP);
-                if (value != NULL && sscanf(value, "%lf", &n->split_ratio) == 1)
-                    arrange(mon, mon->desk);
+        } else if (streq("-e", *args) || streq("--edge", *args)) {
+            num--, args++;
+            if (num < 2)
+                return false;
+            direction_t dir;
+            if (!parse_direction(*args, &dir))
+                return false;
+            num--, args++;
+            fence_move_t fmo;
+            if (parse_fence_move(*args, &fmo)) {
+                move_fence(trg.node, dir, fmo);
+            } else {
+                node_t *n = find_fence(trg.node, dir);
+                if (n == NULL || sscanf(*args, "%lf", &n->split_ratio) != 1)
+                    return false;
             }
+            dirty = true;
+        } else if (streq("-r", *args) || streq("--ratio", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            if (sscanf(*args, "%lf", &trg.node->split_ratio) == 1)
+                window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
+            else
+                return false;
+        } else if (streq("-c", *args) || streq("--close", *args)) {
+            if (num > 1)
+                return false;
+            window_close(trg.node);
+        } else if (streq("-k", *args) || streq("--kill", *args)) {
+            if (num > 1)
+                return false;
+            window_kill(trg.desktop, trg.node);
+            dirty = true;
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "drop_to_monitor") == 0) {
-        if (mon->desk->focus == NULL)
-            return;
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            cycle_dir_t d;
-            if (parse_cycle_direction(dir, &d)) {
-                monitor_t *m;
-                if (d == CYCLE_NEXT)
-                    m = ((mon->next == NULL ? mon_head : mon->next));
-                else
-                    m = ((mon->prev == NULL ? mon_tail : mon->prev));
-                transfer_node(mon, mon->desk, m, m->desk, mon->desk->focus);
-                char *opt = strtok(NULL, TOK_SEP);
-                send_option_t o;
-                if (parse_send_option(opt, &o) && o == SEND_OPTION_FOLLOW)
-                    focus_node(m, m->desk, m->desk->focus);
+        num--, args++;
+    }
+
+    if (dirty)
+        arrange(trg.monitor, trg.desktop);
+
+    return true;
+}
+
+bool cmd_desktop(char **args, int num)
+{
+    coordinates_t ref = {mon, mon->desk, NULL};
+    coordinates_t trg = ref;
+
+    if (*args[0] != OPT_CHR) {
+        if (desktop_from_desc(*args, &ref, &trg))
+            num--, args++;
+        else
+            return false;
+    }
+
+    bool dirty = false;
+
+    while (num > 0) {
+        if (streq("-f", *args) || streq("--focus", *args)) {
+            coordinates_t dst = trg;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!desktop_from_desc(*args, &trg, &dst))
+                    return false;
             }
-        }
-    } else if (strcmp(cmd, "send_to_monitor") == 0) {
-        if (mon->desk->focus == NULL)
-            return;
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            monitor_t *m = find_monitor(name);
-            if (m != NULL && m != mon) {
-                transfer_node(mon, mon->desk, m, m->desk, mon->desk->focus);
-                char *opt = strtok(NULL, TOK_SEP);
-                send_option_t o;
-                if (parse_send_option(opt, &o) && o == SEND_OPTION_FOLLOW)
-                    focus_node(m, m->desk, m->desk->focus);
+            if (auto_alternate && dst.desktop == dst.monitor->desk && dst.monitor->last_desk != NULL)
+                dst.desktop = dst.monitor->last_desk;
+            focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
+        } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+            num--, args++;
+            if (num < 1 || trg.monitor->desk_head == trg.monitor->desk_tail)
+                return false;
+            coordinates_t dst;
+            if (monitor_from_desc(*args, &trg, &dst)) {
+                transfer_desktop(trg.monitor, dst.monitor, dst.desktop);
+                trg.monitor = dst.monitor;
+                update_current();
+            } else {
+                return false;
             }
-        }
-    } else if (strcmp(cmd, "drop_to") == 0) {
-        if (mon->desk->focus == NULL)
-            return;
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            cycle_dir_t c;
-            if (parse_cycle_direction(dir, &c)) {
-                desktop_t *d;
-                if (c == CYCLE_NEXT)
-                    d = ((mon->desk->next == NULL ? mon->desk_head : mon->desk->next));
-                else
-                    d = ((mon->desk->prev == NULL ? mon->desk_tail : mon->desk->prev));
-                transfer_node(mon, mon->desk, mon, d, mon->desk->focus);
-                char *opt = strtok(NULL, TOK_SEP);
-                send_option_t o;
-                if (parse_send_option(opt, &o) && o == SEND_OPTION_FOLLOW)
-                    focus_node(mon, d, d->focus);
+        } else if (streq("-l", *args) || streq("--layout", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            layout_t lyt;
+            cycle_dir_t cyc;
+            if (parse_cycle_direction(*args, &cyc))
+                change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
+            else if (parse_layout(*args, &lyt))
+                change_layout(trg.monitor, trg.desktop, lyt);
+            else
+                return false;
+        } else if (streq("-n", *args) || streq("--rename", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            strncpy(trg.desktop->name, *args, sizeof(trg.desktop->name));
+            ewmh_update_desktop_names();
+            put_status();
+        } else if (streq("-r", *args) || streq("--rm", *args)) {
+            if (trg.desktop->root == NULL
+                    && trg.monitor->desk_head != trg.monitor->desk_tail) {
+                remove_desktop(trg.monitor, trg.desktop);
+                desktop_show(trg.monitor->desk);
+                update_current();
+                return true;
+            } else {
+                return false;
             }
-        }
-    } else if (strcmp(cmd, "send_to") == 0) {
-        if (mon->desk->focus == NULL)
-            return;
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            desktop_location_t loc;
-            if (locate_desktop(name, &loc)) {
-                transfer_node(mon, mon->desk, loc.monitor, loc.desktop, mon->desk->focus);
-                char *opt = strtok(NULL, TOK_SEP);
-                send_option_t o;
-                if (parse_send_option(opt, &o) && o == SEND_OPTION_FOLLOW)
-                    focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
+        } else if (streq("-c", *args) || streq("--cancel-presel", *args)) {
+            reset_mode(&trg);
+        } else if (streq("-F", *args) || streq("--flip", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            flip_t flp;
+            if (parse_flip(*args, &flp)) {
+                flip_tree(trg.desktop->root, flp);
+                dirty = true;
             }
-        }
-    } else if (strcmp(cmd, "rename_monitor") == 0) {
-        char *cur_name = strtok(NULL, TOK_SEP);
-        if (cur_name != NULL) {
-            monitor_t *m = find_monitor(cur_name);
-            if (m != NULL) {
-                char *new_name = strtok(NULL, TOK_SEP);
-                if (new_name != NULL) {
-                    strncpy(m->name, new_name, sizeof(m->name));
-                    put_status();
-                }
+        } else if (streq("-R", *args) || streq("--rotate", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            int rot = atoi(*args);
+            while (rot < 0)
+                rot += 360;
+            while (rot > 359)
+                rot -= 360;
+            if ((rot % 90) != 0) {
+                return false;
+            } else {
+                rotate_tree(trg.desktop->root, rot);
+                dirty = true;
             }
-        }
-    } else if (strcmp(cmd, "rename") == 0) {
-        char *cur_name = strtok(NULL, TOK_SEP);
-        if (cur_name != NULL) {
-            desktop_location_t loc;
-            if (locate_desktop(cur_name, &loc)) {
-                char *new_name = strtok(NULL, TOK_SEP);
-                if (new_name != NULL) {
-                    strncpy(loc.desktop->name, new_name, sizeof(loc.desktop->name));
-                    ewmh_update_desktop_names();
-                    put_status();
-                }
+        } else if (streq("-B", *args) || streq("--balance", *args)) {
+            balance_tree(trg.desktop->root);
+            dirty = true;
+        } else if (streq("-C", *args) || streq("--circulate", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            circulate_dir_t cir;
+            if (parse_circulate_direction(*args, &cir)) {
+                circulate_leaves(trg.monitor, trg.desktop, cir);
+                dirty = true;
+            } else {
+                return false;
             }
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "use_monitor") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            monitor_t *m = find_monitor(name);
-            if (m != NULL) {
-                if (auto_alternate && m == mon && last_mon != NULL)
-                    m = last_mon;
-                focus_node(m, m->desk, m->desk->focus);
+        num--, args++;
+    }
+
+    if (dirty)
+        arrange(trg.monitor, trg.desktop);
+
+    return true;
+}
+
+bool cmd_monitor(char **args, int num)
+{
+    coordinates_t ref = {mon, NULL, NULL};
+    coordinates_t trg = ref;
+
+    if (*args[0] != OPT_CHR) {
+        if (monitor_from_desc(*args, &ref, &trg))
+            num--, args++;
+        else
+            return false;
+    }
+
+    while (num > 0) {
+        if (streq("-f", *args) || streq("--focus", *args)) {
+            coordinates_t dst = trg;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!desktop_from_desc(*args, &trg, &dst))
+                    return false;
             }
-        }
-    } else if (strcmp(cmd, "focus_monitor") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            direction_t d;
-            if (parse_direction(dir, &d)) {
-                monitor_t *m = nearest_monitor(d);
-                if (m != NULL)
-                    focus_node(m, m->desk, m->desk->focus);
+            if (auto_alternate && dst.monitor == mon && last_mon != NULL)
+                dst.monitor = last_mon;
+            focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
+        } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            while (num > 0) {
+                add_desktop(trg.monitor, make_desktop(*args));
+                num--, args++;
             }
-        }
-    } else if (strcmp(cmd, "use") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            desktop_location_t loc;
-            if (locate_desktop(name, &loc)) {
-                if (auto_alternate && loc.desktop == mon->desk && mon->last_desk != NULL)
-                    focus_node(mon, mon->last_desk, mon->last_desk->focus);
-                else
-                    focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
+        } else if (streq("-r", *args) || streq("--remove-desktops", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            while (num > 0) {
+                coordinates_t dst;
+                if (locate_desktop(*args, &dst) && dst.monitor->desk_head != dst.monitor->desk_tail) {
+                    remove_desktop(dst.monitor, dst.desktop);
+                    desktop_show(dst.monitor->desk);
+                }
+                num--, args++;
             }
+        } else if (streq("-p", *args) || streq("--pad", *args)) {
+            num--, args++;
+            if (num < 4)
+                return false;
+            char values[MAXLEN];
+            snprintf(values, sizeof(values), "%s %s %s %s", args[0], args[1], args[2], args[3]);
+            if (sscanf(values, "%i %i %i %i", &trg.monitor->top_padding, &trg.monitor->right_padding, &trg.monitor->bottom_padding, &trg.monitor->left_padding) == 4)
+                for (desktop_t *d = trg.monitor->desk_head; d != NULL; d = d->next)
+                    arrange(trg.monitor, d);
+            else
+                return false;
+        } else if (streq("-n", *args) || streq("--rename", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            strncpy(trg.monitor->name, *args, sizeof(trg.monitor->name));
+            put_status();
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "cycle_monitor") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            cycle_dir_t d;
-            if (parse_cycle_direction(dir, &d))
-                cycle_monitor(d);
-        }
-    } else if (strcmp(cmd, "cycle_desktop") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            cycle_dir_t d;
-            if (parse_cycle_direction(dir, &d)) {
-                skip_desktop_t k;
-                char *skip = strtok(NULL, TOK_SEP);
-                if (parse_skip_desktop(skip, &k))
-                    cycle_desktop(mon, mon->desk, d, k);
+        num--, args++;
+    }
+
+    return true;
+}
+
+bool cmd_query(char **args, int num, char *rsp) {
+    coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+    coordinates_t trg = {NULL, NULL, NULL};
+    domain_t dom = DOMAIN_TREE;
+    int d = 0, t = 0;
+
+    while (num > 0) {
+        if (streq("-T", *args) || streq("--tree", *args)) {
+            dom = DOMAIN_TREE, d++;
+        } else if (streq("-M", *args) || streq("--monitors", *args)) {
+            dom = DOMAIN_MONITOR, d++;
+        } else if (streq("-D", *args) || streq("--desktops", *args)) {
+            dom = DOMAIN_DESKTOP, d++;
+        } else if (streq("-W", *args) || streq("--windows", *args)) {
+            dom = DOMAIN_WINDOW, d++;
+        } else if (streq("-H", *args) || streq("--history", *args)) {
+            dom = DOMAIN_HISTORY, d++;
+        } else if (streq("-m", *args) || streq("--monitor", *args)) {
+            trg.monitor = ref.monitor;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!monitor_from_desc(*args, &ref, &trg))
+                    return false;
             }
-        }
-    } else if (strcmp(cmd, "cycle") == 0) {
-        if (mon->desk->focus != NULL && mon->desk->focus->client->fullscreen)
-            return;
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            cycle_dir_t d;
-            if (parse_cycle_direction(dir, &d)) {
-                skip_client_t k;
-                char *skip = strtok(NULL, TOK_SEP);
-                if (parse_skip_client(skip, &k))
-                    cycle_leaf(mon, mon->desk, mon->desk->focus, d, k);
+            t++;
+        } else if (streq("-d", *args) || streq("--desktop", *args)) {
+            trg.monitor = ref.monitor;
+            trg.desktop = ref.desktop;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!desktop_from_desc(*args, &ref, &trg))
+                    return false;
             }
-        }
-    } else if (strcmp(cmd, "nearest") == 0) {
-        if (mon->desk->focus != NULL && mon->desk->focus->client->fullscreen)
-            return;
-        char *arg = strtok(NULL, TOK_SEP);
-        if (arg != NULL) {
-            nearest_arg_t a;
-            if (parse_nearest_argument(arg, &a)) {
-                skip_client_t k;
-                char *skip = strtok(NULL, TOK_SEP);
-                if (parse_skip_client(skip, &k))
-                    nearest_leaf(mon, mon->desk, mon->desk->focus, a, k);
+            t++;
+        } else if (streq("-w", *args) || streq("--window", *args)) {
+            trg = ref;
+            if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                num--, args++;
+                if (!node_from_desc(*args, &ref, &trg))
+                    return false;
             }
+            t++;
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "biggest") == 0) {
-        node_t *n = find_biggest(mon->desk);
-        if (n != NULL)
-            snprintf(rsp, BUFSIZ, "0x%X", n->client->window);
-    } else if (strcmp(cmd, "circulate") == 0) {
-        if (mon->desk->layout == LAYOUT_MONOCLE
-                || (mon->desk->focus != NULL && !is_tiled(mon->desk->focus->client)))
-            return;
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            circulate_dir_t d;
-            if (parse_circulate_direction(dir, &d))
-                circulate_leaves(mon, mon->desk, d);
-        }
-        arrange(mon, mon->desk);
-    } else if (strcmp(cmd, "rule") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
+        num--, args++;
+    }
+
+    if (d != 1 || t > 1)
+        return false;
+
+    if (dom == DOMAIN_HISTORY)
+        query_history(trg, rsp);
+    else if (dom == DOMAIN_WINDOW)
+        query_windows(trg, rsp);
+    else
+        query_monitors(trg, dom, rsp);
+
+    return true;
+}
+
+bool cmd_rule(char **args, int num, char *rsp) {
+    while (num > 0) {
+        if (streq("-a", *args) || streq("--add", *args)) {
+            num--, args++;
+            if (num < 2)
+                return false;
             rule_t *rule = make_rule();
-            strncpy(rule->cause.name, name, sizeof(rule->cause.name));
-            char *arg = strtok(NULL, TOK_SEP);
-            while (arg != NULL) {
-                if (strcmp(arg, "floating") == 0) {
+            strncpy(rule->cause.name, *args, sizeof(rule->cause.name));
+            num--, args++;
+            while (num > 0) {
+                if (streq("--floating", *args)) {
                     rule->effect.floating = true;
-                } else if (strcmp(arg, "follow") == 0) {
+                } else if (streq("--follow", *args)) {
                     rule->effect.follow = true;
-                } else {
-                    desktop_location_t loc;
-                    if (locate_desktop(arg, &loc)) {
-                        rule->effect.monitor = loc.monitor;
-                        rule->effect.desktop = loc.desktop;
+                } else if (streq("-d", *args) || streq("--desktop", *args)) {
+                    num--, args++;
+                    if (num < 1) {
+                        free(rule);
+                        return false;
                     }
+                    strncpy(rule->effect.desc, *args, sizeof(rule->effect.desc));
+                } else {
+                    free(rule);
+                    return false;
                 }
-                arg = strtok(NULL, TOK_SEP);
+                num--, args++;
             }
             add_rule(rule);
+        } else if (streq("-r", *args) || streq("--rm", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            unsigned int uid;
+            while (num > 0) {
+                if (sscanf(*args, "%X", &uid) == 1)
+                    remove_rule_by_uid(uid);
+                else
+                    return false;
+                num--, args++;
+            }
+        } else if (streq("-l", *args) || streq("--list", *args)) {
+            num--, args++;
+            list_rules(num > 0 ? *args : NULL, rsp);
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "remove_rule") == 0) {
-        char *arg;
-        unsigned int uid;
-        while ((arg = strtok(NULL, TOK_SEP)) != NULL)
-            if (sscanf(arg, "%X", &uid) > 0)
-                remove_rule_by_uid(uid);
-    } else if (strcmp(cmd, "swap") == 0) {
-        char *opt = strtok(NULL, TOK_SEP);
-        swap_option_t o;
-        if (!parse_swap_option(opt, &o))
-            return;
-        node_t *last_focus = history_get(mon->desk->history, 1);
-        swap_nodes(mon->desk->focus, last_focus, true);
-        arrange(mon, mon->desk);
-        if (o == SWAP_OPTION_SWAP_FOCUS)
-            focus_node(mon, mon->desk, last_focus);
-    } else if (strcmp(cmd, "alternate") == 0) {
-        focus_node(mon, mon->desk, history_get(mon->desk->history, 1));
-    } else if (strcmp(cmd, "alternate_desktop") == 0) {
-        if (mon->last_desk != NULL)
-            focus_node(mon, mon->last_desk, mon->last_desk->focus);
-    } else if (strcmp(cmd, "alternate_monitor") == 0) {
-        if (last_mon != NULL)
-            focus_node(last_mon, last_mon->desk, last_mon->desk->focus);
-    } else if (strcmp(cmd, "add_in") == 0) {
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            monitor_t *m = find_monitor(name);
-            if (m != NULL)
-                for (name = strtok(NULL, TOK_SEP); name != NULL; name = strtok(NULL, TOK_SEP))
-                    add_desktop(m, make_desktop(name));
+        num--, args++;
+    }
+
+    return true;
+}
+
+bool cmd_pointer(char **args, int num) {
+    while (num > 0) {
+        if (streq("-t", *args) || streq("--track", *args)) {
+            num--, args++;
+            if (num < 2)
+                return false;
+            int x, y;
+            if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1)
+                track_pointer(x, y);
+            else
+                return false;
+        } else if (streq("-g", *args) || streq("--grab", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            pointer_action_t pac;
+            if (parse_pointer_action(*args, &pac))
+                grab_pointer(pac);
+            else
+                return false;
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "add") == 0) {
-        for (char *name = strtok(NULL, TOK_SEP); name != NULL; name = strtok(NULL, TOK_SEP))
-            add_desktop(mon, make_desktop(name));
-    } else if (strcmp(cmd, "remove_desktop") == 0) {
-        for (char *name = strtok(NULL, TOK_SEP); name != NULL; name = strtok(NULL, TOK_SEP)) {
-            desktop_location_t loc;
-            if (locate_desktop(name, &loc)) {
-                if (loc.desktop->root == NULL && loc.monitor->desk_head != loc.monitor->desk_tail) {
-                    remove_desktop(loc.monitor, loc.desktop);
-                    desktop_show(loc.monitor->desk);
-                }
-            }
+        num--, args++;
+    }
+
+    return true;
+}
+
+bool cmd_restore(char **args, int num) {
+    while (num > 0) {
+        if (streq("-T", *args) || streq("--tree", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            restore_tree(*args);
+        } else if (streq("-H", *args) || streq("--history", *args)) {
+            num--, args++;
+            if (num < 1)
+                return false;
+            restore_history(*args);
+        } else {
+            return false;
         }
-        update_current();
-    } else if (strcmp(cmd, "send_desktop_to") == 0) {
-        if (mon->desk_head == mon->desk_tail)
-            return;
-        char *name = strtok(NULL, TOK_SEP);
-        if (name != NULL) {
-            monitor_t *m = find_monitor(name);
-            if (m != NULL && m != mon) {
-                char *opt = strtok(NULL, TOK_SEP);
-                send_option_t o;
-                if (!parse_send_option(opt, &o))
-                    return;
-                desktop_t *d = mon->desk;
-                transfer_desktop(mon, m, d);
-                if (o == SEND_OPTION_FOLLOW)
-                    focus_node(m, d, d->focus);
-                else if (o == SEND_OPTION_DONT_FOLLOW)
-                    update_current();
-            }
+        num--, args++;
+    }
+
+    return true;
+}
+
+bool cmd_control(char **args, int num) {
+    while (num > 0) {
+        if (streq("--adopt-orphans", *args)) {
+            adopt_orphans();
+        } else if (streq("--put-status", *args)) {
+            put_status();
+        } else if (streq("--toggle-visibility", *args)) {
+            toggle_visibility();
+        } else {
+            return false;
         }
-    } else if (strcmp(cmd, "focus") == 0) {
-        char *dir = strtok(NULL, TOK_SEP);
-        if (dir != NULL) {
-            direction_t d;
-            if (parse_direction(dir, &d)) {
-                node_t *n = nearest_neighbor(mon->desk, mon->desk->focus, d);
-                if (n != NULL) {
-                    focus_node(mon, mon->desk, n);
-                } else if (monitor_focus_fallback) {
-                    monitor_t *m = nearest_monitor(d);
-                    if (m != NULL)
-                        focus_node(m, m->desk, m->desk->focus);
-                }
+        num--, args++;
+    }
+
+    return true;
+}
+
+bool cmd_config(char **args, int num, char *rsp) {
+    if (num == 2)
+        return set_setting(*args, *(args + 1));
+    else if (num == 1)
+        return get_setting(*args, rsp);
+    else
+        return false;
+}
+
+bool cmd_quit(char **args, int num) {
+    if (num > 0 && sscanf(*args, "%i", &exit_status) != 1)
+        return false;
+    quit();
+    return true;
+}
+
+bool handle_message(char *msg, int msg_len, char *rsp)
+{
+    int cap = INIT_CAP;
+    int num = 0;
+    char **args = malloc(cap * sizeof(char *));
+    if (args == NULL)
+        return false;
+
+    for (int i = 0, j = 0; i < msg_len; i++) {
+        if (msg[i] == 0) {
+            args[num++] = msg + j;
+            j = i + 1;
+        }
+        if (num >= cap) {
+            cap *= 2;
+            char **new = realloc(args, cap * sizeof(char *));
+            if (new == NULL) {
+                free(args);
+                return false;
+            } else {
+                args = new;
             }
         }
-    } else if (strcmp(cmd, "put_status") == 0) {
-        put_status();
-    } else if (strcmp(cmd, "adopt_orphans") == 0) {
-        adopt_orphans();
-    } else if (strcmp(cmd, "restore_layout") == 0) {
-        char *arg = strtok(NULL, TOK_SEP);
-        restore_layout(arg);
-    } else if (strcmp(cmd, "restore_history") == 0) {
-        char *arg = strtok(NULL, TOK_SEP);
-        restore_history(arg);
-    } else if (strcmp(cmd, "quit") == 0) {
-        char *arg = strtok(NULL, TOK_SEP);
-        if (arg != NULL)
-            sscanf(arg, "%i", &exit_status);
-        quit();
-    } else {
-        snprintf(rsp, BUFSIZ, "unknown command: %s", cmd);
     }
+
+    if (num < 1)
+        return false;
+
+    char **args_orig = args;
+    bool ret = process_message(args, num, rsp);
+    free(args_orig);
+    return ret;
 }
 
-void set_setting(char *name, char *value, char *rsp)
+bool process_message(char **args, int num, char *rsp)
 {
-    if (name == NULL || value == NULL)
-        return;
-
-    if (strcmp(name, "border_width") == 0) {
-        sscanf(value, "%u", &border_width);
-    } else if (strcmp(name, "window_gap") == 0) {
-        sscanf(value, "%i", &window_gap);
-    } else if (strcmp(name, "split_ratio") == 0) {
-        sscanf(value, "%lf", &split_ratio);
-    } else if (strcmp(name, "left_padding") == 0) {
-        sscanf(value, "%i", &mon->left_padding);
-    } else if (strcmp(name, "right_padding") == 0) {
-        sscanf(value, "%i", &mon->right_padding);
-    } else if (strcmp(name, "top_padding") == 0) {
-        sscanf(value, "%i", &mon->top_padding);
-    } else if (strcmp(name, "bottom_padding") == 0) {
-        sscanf(value, "%i", &mon->bottom_padding);
-    } else if (strcmp(name, "focused_border_color") == 0) {
-        strncpy(focused_border_color, value, sizeof(focused_border_color));
-        focused_border_color_pxl = get_color(focused_border_color);
-    } else if (strcmp(name, "active_border_color") == 0) {
-        strncpy(active_border_color, value, sizeof(active_border_color));
-        active_border_color_pxl = get_color(active_border_color);
-    } else if (strcmp(name, "normal_border_color") == 0) {
-        strncpy(normal_border_color, value, sizeof(normal_border_color));
-        normal_border_color_pxl = get_color(normal_border_color);
-    } else if (strcmp(name, "presel_border_color") == 0) {
-        strncpy(presel_border_color, value, sizeof(presel_border_color));
-        presel_border_color_pxl = get_color(presel_border_color);
-    } else if (strcmp(name, "focused_locked_border_color") == 0) {
-        strncpy(focused_locked_border_color, value, sizeof(focused_locked_border_color));
-        focused_locked_border_color_pxl = get_color(focused_locked_border_color);
-    } else if (strcmp(name, "active_locked_border_color") == 0) {
-        strncpy(active_locked_border_color, value, sizeof(active_locked_border_color));
-        active_locked_border_color_pxl = get_color(active_locked_border_color);
-    } else if (strcmp(name, "normal_locked_border_color") == 0) {
-        strncpy(normal_locked_border_color, value, sizeof(normal_locked_border_color));
-        normal_locked_border_color_pxl = get_color(normal_locked_border_color);
-    } else if (strcmp(name, "urgent_border_color") == 0) {
-        strncpy(urgent_border_color, value, sizeof(urgent_border_color));
-        urgent_border_color_pxl = get_color(urgent_border_color);
-    } else if (strcmp(name, "borderless_monocle") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            borderless_monocle = b;
-    } else if (strcmp(name, "gapless_monocle") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            gapless_monocle = b;
-    } else if (strcmp(name, "focus_follows_pointer") == 0) {
+    if (streq("window", *args)) {
+        return cmd_window(++args, --num);
+    } else if (streq("desktop", *args)) {
+        return cmd_desktop(++args, --num);
+    } else if (streq("monitor", *args)) {
+        return cmd_monitor(++args, --num);
+    } else if (streq("query", *args)) {
+        return cmd_query(++args, --num, rsp);
+    } else if (streq("restore", *args)) {
+        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 cmd_config(++args, --num, rsp);
+    } else if (streq("quit", *args)) {
+        return cmd_quit(++args, --num);
+    }
+
+    return false;
+}
+
+bool set_setting(char *name, char *value)
+{
+    if (streq("border_width", name)) {
+        if (sscanf(value, "%u", &border_width) != 1)
+            return false;
+    } else if (streq("window_gap", name)) {
+        if (sscanf(value, "%i", &window_gap) != 1)
+            return false;
+    } else if (streq("split_ratio", name)) {
+        if (sscanf(value, "%lf", &split_ratio) != 1)
+            return false;
+    } else if (streq("left_padding", name)) {
+        if (sscanf(value, "%i", &mon->left_padding) != 1)
+            return false;
+    } else if (streq("right_padding", name)) {
+        if (sscanf(value, "%i", &mon->right_padding) != 1)
+            return false;
+    } else if (streq("top_padding", name)) {
+        if (sscanf(value, "%i", &mon->top_padding) != 1)
+            return false;
+    } else if (streq("bottom_padding", name)) {
+        if (sscanf(value, "%i", &mon->bottom_padding) != 1)
+            return false;
+#define SETCOLOR(s) \
+    } else if (streq(#s, name)) { \
+        if (get_color(value, &s ## _pxl)) \
+            strncpy(s, value, sizeof(s)); \
+        else \
+            return false;
+    SETCOLOR(focused_border_color)
+    SETCOLOR(active_border_color)
+    SETCOLOR(normal_border_color)
+    SETCOLOR(presel_border_color)
+    SETCOLOR(focused_locked_border_color)
+    SETCOLOR(active_locked_border_color)
+    SETCOLOR(normal_locked_border_color)
+    SETCOLOR(urgent_border_color)
+#undef SETCOLOR
+    } else if (streq("focus_follows_pointer", name)) {
         bool b;
         if (parse_bool(value, &b) && b != focus_follows_pointer) {
             uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK : CLIENT_EVENT_MASK_FFP)};
@@ -581,131 +680,96 @@ void set_setting(char *name, char *value, char *rsp)
             else
                 enable_motion_recorder();
             focus_follows_pointer = b;
+            return true;
+        } else {
+            return false;
         }
-        return;
-    } else if (strcmp(name, "pointer_follows_monitor") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            pointer_follows_monitor = b;
-        return;
-    } else if (strcmp(name, "monitor_focus_fallback") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            monitor_focus_fallback = b;
-        return;
-    } else if (strcmp(name, "adaptative_raise") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            adaptative_raise = b;
-        return;
-    } else if (strcmp(name, "apply_shadow_property") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            apply_shadow_property = b;
-        return;
-    } else if (strcmp(name, "auto_alternate") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            auto_alternate = b;
-        return;
-    } else if (strcmp(name, "auto_cancel") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            auto_cancel = b;
-        return;
-    } else if (strcmp(name, "focus_by_distance") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            focus_by_distance = b;
-        return;
-    } else if (strcmp(name, "history_aware_focus") == 0) {
-        bool b;
-        if (parse_bool(value, &b))
-            history_aware_focus = b;
-        return;
-    } else if (strcmp(name, "wm_name") == 0) {
+#define SETBOOL(s) \
+    } else if (streq(#s, name)) { \
+        if (!parse_bool(value, &s)) \
+            return false;
+        SETBOOL(borderless_monocle)
+        SETBOOL(gapless_monocle)
+        SETBOOL(pointer_follows_monitor)
+        SETBOOL(monitor_focus_fallback)
+        SETBOOL(adaptative_raise)
+        SETBOOL(apply_shadow_property)
+        SETBOOL(auto_alternate)
+        SETBOOL(auto_cancel)
+        SETBOOL(focus_by_distance)
+        SETBOOL(history_aware_focus)
+#undef SETBOOL
+    } else if (streq("wm_name", name)) {
         strncpy(wm_name, value, sizeof(wm_name));
         ewmh_update_wm_name();
-        return;
+        return true;
     } else {
-        snprintf(rsp, BUFSIZ, "unknown setting: %s", name);
-        return;
+        return false;
     }
 
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
             arrange(m, d);
+
+    return true;
 }
 
-void get_setting(char *name, char* rsp)
+bool get_setting(char *name, char* rsp)
 {
-    if (name == NULL)
-        return;
-
-    if (strcmp(name, "border_width") == 0)
+    if (streq("border_width", name))
         snprintf(rsp, BUFSIZ, "%u", border_width);
-    else if (strcmp(name, "window_gap") == 0)
-        snprintf(rsp, BUFSIZ, "%i", window_gap);
-    else if (strcmp(name, "split_ratio") == 0)
+    else if (streq("split_ratio", name))
         snprintf(rsp, BUFSIZ, "%lf", split_ratio);
-    else if (strcmp(name, "left_padding") == 0)
+    else if (streq("window_gap", name))
+        snprintf(rsp, BUFSIZ, "%i", window_gap);
+    else if (streq("left_padding", name))
         snprintf(rsp, BUFSIZ, "%i", mon->left_padding);
-    else if (strcmp(name, "right_padding") == 0)
+    else if (streq("right_padding", name))
         snprintf(rsp, BUFSIZ, "%i", mon->right_padding);
-    else if (strcmp(name, "top_padding") == 0)
+    else if (streq("top_padding", name))
         snprintf(rsp, BUFSIZ, "%i", mon->top_padding);
-    else if (strcmp(name, "bottom_padding") == 0)
+    else if (streq("bottom_padding", name))
         snprintf(rsp, BUFSIZ, "%i", mon->bottom_padding);
-    else if (strcmp(name, "focused_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", focused_border_color, focused_border_color_pxl);
-    else if (strcmp(name, "active_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", active_border_color, active_border_color_pxl);
-    else if (strcmp(name, "normal_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", normal_border_color, normal_border_color_pxl);
-    else if (strcmp(name, "presel_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", presel_border_color, presel_border_color_pxl);
-    else if (strcmp(name, "focused_locked_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", focused_locked_border_color, focused_locked_border_color_pxl);
-    else if (strcmp(name, "active_locked_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", active_locked_border_color, active_locked_border_color_pxl);
-    else if (strcmp(name, "normal_locked_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", normal_locked_border_color, normal_locked_border_color_pxl);
-    else if (strcmp(name, "urgent_border_color") == 0)
-        snprintf(rsp, BUFSIZ, "%s (%06X)", urgent_border_color, urgent_border_color_pxl);
-    else if (strcmp(name, "borderless_monocle") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(borderless_monocle));
-    else if (strcmp(name, "gapless_monocle") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(gapless_monocle));
-    else if (strcmp(name, "focus_follows_pointer") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(focus_follows_pointer));
-    else if (strcmp(name, "pointer_follows_monitor") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(pointer_follows_monitor));
-    else if (strcmp(name, "monitor_focus_fallback") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(monitor_focus_fallback));
-    else if (strcmp(name, "adaptative_raise") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(adaptative_raise));
-    else if (strcmp(name, "apply_shadow_property") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(apply_shadow_property));
-    else if (strcmp(name, "auto_alternate") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(auto_alternate));
-    else if (strcmp(name, "auto_cancel") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(auto_cancel));
-    else if (strcmp(name, "focus_by_distance") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(focus_by_distance));
-    else if (strcmp(name, "history_aware_focus") == 0)
-        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(history_aware_focus));
-    else if (strcmp(name, "wm_name") == 0)
+#define GETCOLOR(s) \
+    else if (streq(#s, name)) \
+        snprintf(rsp, BUFSIZ, "%s (%06X)", s, s##_pxl);
+    GETCOLOR(focused_border_color)
+    GETCOLOR(active_border_color)
+    GETCOLOR(normal_border_color)
+    GETCOLOR(presel_border_color)
+    GETCOLOR(focused_locked_border_color)
+    GETCOLOR(active_locked_border_color)
+    GETCOLOR(normal_locked_border_color)
+    GETCOLOR(urgent_border_color)
+#undef GETCOLOR
+#define GETBOOL(s) \
+    else if (streq(#s, name)) \
+        snprintf(rsp, BUFSIZ, "%s", BOOLSTR(s));
+    GETBOOL(borderless_monocle)
+    GETBOOL(gapless_monocle)
+    GETBOOL(focus_follows_pointer)
+    GETBOOL(pointer_follows_monitor)
+    GETBOOL(monitor_focus_fallback)
+    GETBOOL(adaptative_raise)
+    GETBOOL(apply_shadow_property)
+    GETBOOL(auto_alternate)
+    GETBOOL(auto_cancel)
+    GETBOOL(focus_by_distance)
+    GETBOOL(history_aware_focus)
+#undef GETBOOL
+    else if (streq("wm_name", name))
         snprintf(rsp, BUFSIZ, "%s", wm_name);
     else
-        snprintf(rsp, BUFSIZ, "unknown setting: %s", name);
+        return false;
+    return true;
 }
 
 bool parse_bool(char *value, bool *b)
 {
-    if (strcmp(value, "true") == 0) {
+    if (streq("true", value) || streq("on", value)) {
         *b = true;
         return true;
-    } else if (strcmp(value, "false") == 0) {
+    } else if (streq("false", value) || streq("off", value)) {
         *b = false;
         return true;
     }
@@ -714,10 +778,10 @@ bool parse_bool(char *value, bool *b)
 
 bool parse_layout(char *s, layout_t *l)
 {
-    if (strcmp(s, "monocle") == 0) {
+    if (streq("monocle", s)) {
         *l = LAYOUT_MONOCLE;
         return true;
-    } else if (strcmp(s, "tiled") == 0) {
+    } else if (streq("tiled", s)) {
         *l = LAYOUT_TILED;
         return true;
     }
@@ -726,29 +790,17 @@ bool parse_layout(char *s, layout_t *l)
 
 bool parse_direction(char *s, direction_t *d)
 {
-    if (strcmp(s, "up") == 0) {
-        *d = DIR_UP;
+    if (streq("right", s)) {
+        *d = DIR_RIGHT;
         return true;
-    } else if (strcmp(s, "down") == 0) {
+    } else if (streq("down", s)) {
         *d = DIR_DOWN;
         return true;
-    } else if (strcmp(s, "left") == 0) {
+    } else if (streq("left", s)) {
         *d = DIR_LEFT;
         return true;
-    } else if (strcmp(s, "right") == 0) {
-        *d = DIR_RIGHT;
-        return true;
-    }
-    return false;
-}
-
-bool parse_nearest_argument(char *s, nearest_arg_t *a)
-{
-    if (strcmp(s, "older") == 0) {
-        *a = NEAREST_OLDER;
-        return true;
-    } else if (strcmp(s, "newer") == 0) {
-        *a = NEAREST_NEWER;
+    } else if (streq("up", s)) {
+        *d = DIR_UP;
         return true;
     }
     return false;
@@ -756,133 +808,34 @@ bool parse_nearest_argument(char *s, nearest_arg_t *a)
 
 bool parse_cycle_direction(char *s, cycle_dir_t *d)
 {
-    if (strcmp(s, "prev") == 0) {
-        *d = CYCLE_PREV;
-        return true;
-    } else if (strcmp(s, "next") == 0) {
+    if (streq("next", s)) {
         *d = CYCLE_NEXT;
         return true;
+    } else if (streq("prev", s)) {
+        *d = CYCLE_PREV;
+        return true;
     }
     return false;
 }
 
 bool parse_circulate_direction(char *s, circulate_dir_t *d)
 {
-    if (strcmp(s, "forward") == 0) {
+    if (streq("forward", s)) {
         *d = CIRCULATE_FORWARD;
         return true;
-    } else if (strcmp(s, "backward") == 0) {
+    } else if (streq("backward", s)) {
         *d = CIRCULATE_BACKWARD;
         return true;
     }
     return false;
 }
 
-bool parse_skip_client(char *s, skip_client_t *k)
-{
-    if (s == NULL) {
-        *k = CLIENT_SKIP_NONE;
-        return true;
-    } else if (strcmp(s, "--skip-floating") == 0) {
-        *k = CLIENT_SKIP_FLOATING;
-        return true;
-    } else if (strcmp(s, "--skip-tiled") == 0) {
-        *k = CLIENT_SKIP_TILED;
-        return true;
-    } else if (strcmp(s, "--skip-class-equal") == 0) {
-        *k = CLIENT_SKIP_CLASS_EQUAL;
-        return true;
-    } else if (strcmp(s, "--skip-class-differ") == 0) {
-        *k = CLIENT_SKIP_CLASS_DIFFER;
-        return true;
-    }
-    return false;
-}
-
-bool parse_skip_desktop(char *s, skip_desktop_t *k)
-{
-    if (s == NULL) {
-        *k = DESKTOP_SKIP_NONE;
-        return true;
-    } else if (strcmp(s, "--skip-free") == 0) {
-        *k = DESKTOP_SKIP_FREE;
-        return true;
-    } else if (strcmp(s, "--skip-occupied") == 0) {
-        *k = DESKTOP_SKIP_OCCUPIED;
-        return true;
-    }
-    return false;
-}
-
-bool parse_list_option(char *s, list_option_t *o)
-{
-    if (s == NULL) {
-        *o = LIST_OPTION_VERBOSE;
-        return true;
-    } else if (strcmp(s, "--quiet") == 0) {
-        *o = LIST_OPTION_QUIET;
-        return true;
-    }
-    return false;
-}
-
-bool parse_send_option(char *s, send_option_t *o)
-{
-    if (s == NULL) {
-        *o = SEND_OPTION_DONT_FOLLOW;
-        return true;
-    } else if (strcmp(s, "--follow") == 0) {
-        *o = SEND_OPTION_FOLLOW;
-        return true;
-    }
-    return false;
-}
-
-bool parse_swap_option(char *s, swap_option_t *o)
-{
-    if (s == NULL) {
-        *o = SWAP_OPTION_SWAP_FOCUS;
-        return true;
-    } else if (strcmp(s, "--keep-focus") == 0) {
-        *o = SWAP_OPTION_KEEP_FOCUS;
-        return true;
-    }
-    return false;
-}
-
-bool parse_cancel_option(char *s, cancel_option_t *o)
-{
-    if (s == NULL) {
-        *o = CANCEL_OPTION_FOCUSED;
-        return true;
-    } else if (strcmp(s, "--all") == 0) {
-        *o = CANCEL_OPTION_ALL;
-        return true;
-    }
-    return false;
-}
-
-bool parse_rotate(char *s, rotate_t *r)
-{
-    if (strcmp(s, "clockwise") == 0) {
-        *r = ROTATE_CLOCKWISE;
-        return true;
-    } else if (strcmp(s, "counter_clockwise") == 0) {
-        *r = ROTATE_COUNTER_CLOCKWISE;
-        return true;
-    } else if (strcmp(s, "full_cycle") == 0) {
-        *r = ROTATE_FULL_CYCLE;
-        return true;
-    }
-    return false;
-}
-
 bool parse_flip(char *s, flip_t *f)
 {
-    if (strcmp(s, "horizontal") == 0) {
+    if (streq("horizontal", s)) {
         *f = FLIP_HORIZONTAL;
         return true;
-    } else if (strcmp(s, "vertical") == 0) {
+    } else if (streq("vertical", s)) {
         *f = FLIP_VERTICAL;
         return true;
     }
@@ -891,10 +844,10 @@ bool parse_flip(char *s, flip_t *f)
 
 bool parse_fence_move(char *s, fence_move_t *m)
 {
-    if (strcmp(s, "push") == 0) {
+    if (streq("push", s)) {
         *m = MOVE_PUSH;
         return true;
-    } else if (strcmp(s, "pull") == 0) {
+    } else if (streq("pull", s)) {
         *m = MOVE_PULL;
         return true;
     }
@@ -903,18 +856,30 @@ bool parse_fence_move(char *s, fence_move_t *m)
 
 bool parse_pointer_action(char *s, pointer_action_t *a)
 {
-    if (strcmp(s, "move") == 0) {
+    if (streq("move", s)) {
         *a = ACTION_MOVE;
         return true;
-    } else if (strcmp(s, "resize_corner") == 0) {
+    } else if (streq("resize_corner", s)) {
         *a = ACTION_RESIZE_CORNER;
         return true;
-    } else if (strcmp(s, "resize_side") == 0) {
+    } else if (streq("resize_side", s)) {
         *a = ACTION_RESIZE_SIDE;
         return true;
-    } else if (strcmp(s, "focus") == 0) {
+    } else if (streq("focus", s)) {
         *a = ACTION_FOCUS;
         return true;
     }
     return false;
 }
+
+bool parse_window_id(char *s, long int *i)
+{
+    char *end;
+    errno = 0;
+    long int ret = strtol(s, &end, 0);
+    if (errno != 0 || *end != '\0')
+        return false;
+    else
+        *i = ret;
+    return true;
+}
index ed66fb6c4db1b7af849f5968030d702bb15e24ac..89e2cc7a0a14c6e9f4a2666c37b33bf86104434c 100644 (file)
@@ -3,26 +3,32 @@
 
 #include "types.h"
 
-#define TOK_SEP  " "
+#define OPT_CHR  '-'
+#define CAT_CHR  '.'
+#define EQL_TOK  "="
 
-void process_message(char*, char*);
-void get_setting(char*, char*);
-void set_setting(char*, char*, char*);
+bool handle_message(char *, int, char *);
+bool process_message(char **, int, char *);
+bool cmd_window(char **, int);
+bool cmd_desktop(char **, int);
+bool cmd_monitor(char **, int);
+bool cmd_query(char **, int, char *);
+bool cmd_rule(char **, int, char *);
+bool cmd_pointer(char **, int);
+bool cmd_control(char **, int);
+bool cmd_restore(char **, int);
+bool cmd_config(char **, int, char *);
+bool cmd_quit(char **, int);
+bool get_setting(char *, char *);
+bool set_setting(char *, char *);
 bool parse_bool(char *, bool *);
 bool parse_layout(char *, layout_t *);
 bool parse_direction(char *, direction_t *);
-bool parse_nearest_argument(char *, nearest_arg_t *);
 bool parse_cycle_direction(char *, cycle_dir_t *);
 bool parse_circulate_direction(char *, circulate_dir_t *);
-bool parse_list_option(char *, list_option_t *);
-bool parse_send_option(char *, send_option_t *);
-bool parse_swap_option(char *, swap_option_t *);
-bool parse_cancel_option(char *, cancel_option_t *);
-bool parse_skip_client(char *, skip_client_t *);
-bool parse_skip_desktop(char *, skip_desktop_t *);
-bool parse_rotate(char *, rotate_t *);
 bool parse_flip(char *, flip_t *);
 bool parse_fence_move(char *, fence_move_t *);
 bool parse_pointer_action(char *, pointer_action_t *);
+bool parse_window_id(char *, long int *);
 
 #endif
diff --git a/query.c b/query.c
new file mode 100644 (file)
index 0000000..ed1f6e5
--- /dev/null
+++ b/query.c
@@ -0,0 +1,282 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "bspwm.h"
+#include "tree.h"
+#include "settings.h"
+#include "messages.h"
+#include "query.h"
+
+void query_monitors(coordinates_t loc, domain_t dom, char *rsp)
+{
+    char line[MAXLEN];
+    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+        if (loc.monitor != NULL && m != loc.monitor)
+            continue;
+        if (dom != DOMAIN_DESKTOP) {
+            if (dom == DOMAIN_MONITOR) {
+                snprintf(line, sizeof(line), "%s\n", m->name);
+                strncat(rsp, line, REMLEN(rsp));
+                continue;
+            } else {
+                snprintf(line, sizeof(line), "%s %ux%u%+i%+i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y);
+                strncat(rsp, line, REMLEN(rsp));
+                if (m == mon)
+                    strncat(rsp, " #", REMLEN(rsp));
+                else if (m == last_mon)
+                    strncat(rsp, " ~", REMLEN(rsp));
+                strncat(rsp, "\n", REMLEN(rsp));
+            }
+        }
+        query_desktops(m, dom, loc, (dom == DOMAIN_DESKTOP ? 0 : 1), rsp);
+    }
+}
+
+void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, char *rsp)
+{
+    char line[MAXLEN];
+    for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+        if (loc.desktop != NULL && d != loc.desktop)
+            continue;
+        for (unsigned int i = 0; i < depth; i++)
+            strncat(rsp, "  ", REMLEN(rsp));
+        if (dom == DOMAIN_DESKTOP) {
+            snprintf(line, sizeof(line), "%s\n", d->name);
+            strncat(rsp, line, REMLEN(rsp));
+            continue;
+        } else {
+            snprintf(line, sizeof(line), "%s %c", d->name, (d->layout == LAYOUT_TILED ? 'T' : 'M'));
+            strncat(rsp, line, REMLEN(rsp));
+            if (d == m->desk)
+                strncat(rsp, " @", REMLEN(rsp));
+            else if (d == m->last_desk)
+                strncat(rsp, " ~", REMLEN(rsp));
+            strncat(rsp, "\n", REMLEN(rsp));
+        }
+        query_tree(d, d->root, rsp, depth + 1);
+    }
+}
+
+void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
+{
+    if (n == NULL)
+        return;
+
+    char line[MAXLEN];
+
+    for (unsigned int i = 0; i < depth; i++)
+        strncat(rsp, "  ", REMLEN(rsp));
+
+    if (is_leaf(n)) {
+        client_t *c = n->client;
+        snprintf(line, sizeof(line), "%c %s %X %u %ux%u%+i%+i %c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'));
+    } else {
+        snprintf(line, sizeof(line), "%c %c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
+    }
+
+    strncat(rsp, line, REMLEN(rsp));
+
+    if (n == d->focus)
+        strncat(rsp, " *", REMLEN(rsp));
+    strncat(rsp, "\n", REMLEN(rsp));
+
+    query_tree(d, n->first_child, rsp, depth + 1);
+    query_tree(d, n->second_child, rsp, depth + 1);
+}
+
+void query_history(coordinates_t loc, char *rsp)
+{
+    char line[MAXLEN];
+    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+        if (loc.monitor != NULL && m != loc.monitor)
+            continue;
+        for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+            if (loc.desktop != NULL && d != loc.desktop)
+                continue;
+            snprintf(line, sizeof(line), "%s\n", d->name);
+            strncat(rsp, line, REMLEN(rsp));
+            for (node_list_t *a = d->history->tail; a != NULL; a = a->prev) {
+                snprintf(line, sizeof(line), "  %X\n", a->node->client->window);
+                strncat(rsp, line, REMLEN(rsp));
+            }
+        }
+    }
+}
+
+void query_windows(coordinates_t loc, char *rsp)
+{
+    char line[MAXLEN];
+
+    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+        if (loc.monitor != NULL && m != loc.monitor)
+            continue;
+        for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+            if (loc.desktop != NULL && d != loc.desktop)
+                continue;
+            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                if (loc.node != NULL && n != loc.node)
+                    continue;
+                snprintf(line, sizeof(line), "0x%X\n", n->client->window);
+                strncat(rsp, line, REMLEN(rsp));
+            }
+        }
+    }
+}
+
+bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+    client_select_t sel;
+    sel.type = CLIENT_TYPE_ALL;
+    sel.class = CLIENT_CLASS_ALL;
+    char *tok;
+    while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+        tok[0] = '\0';
+        tok++;
+        if (streq("tiled", tok)) {
+            sel.type = CLIENT_TYPE_TILED;
+        } else if (streq("floating", tok)) {
+            sel.type = CLIENT_TYPE_FLOATING;
+        } else if (streq("like", tok)) {
+            sel.class = CLIENT_CLASS_EQUAL;
+        } else if (streq("unlike", tok)) {
+            sel.class = CLIENT_CLASS_DIFFER;
+        }
+    }
+
+    dst->monitor = ref->monitor;
+    dst->desktop = ref->desktop;
+    dst->node = NULL;
+
+    direction_t dir;
+    cycle_dir_t cyc;
+    if (parse_direction(desc, &dir)) {
+        dst->node = nearest_neighbor(dst->desktop, ref->node, dir);
+        if (dst->node == NULL && monitor_focus_fallback) {
+            if (monitor_from_desc(desc, ref, dst)) {
+                dst->desktop = dst->monitor->desk;
+                dst->node = dst->monitor->desk->focus;
+                return true;
+            }
+        }
+    } else if (parse_cycle_direction(desc, &cyc)) {
+        dst->node = closest_node(ref->desktop, ref->node, cyc, sel);
+    } else if (streq("last", desc)) {
+        dst->node = history_get(ref->desktop->history, 1);
+    } else if (streq("biggest", desc)) {
+        dst->node = find_biggest(ref->desktop);
+    } else if (streq("focused", desc)) {
+        dst->monitor = mon;
+        dst->desktop = mon->desk;
+        dst->node = mon->desk->focus;
+    } else {
+        long int wid;
+        if (parse_window_id(desc, &wid))
+            locate_window(wid, dst);
+    }
+
+    return (dst->node != NULL);
+}
+
+bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+    desktop_select_t sel;
+    sel = DESKTOP_ALL;
+    char *tok;
+    while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+        tok[0] = '\0';
+        tok++;
+        if (streq("free", tok)) {
+            sel = DESKTOP_FREE;
+        } else if (streq("occupied", tok)) {
+            sel = DESKTOP_OCCUPIED;
+        }
+    }
+
+    dst->desktop = NULL;
+
+    cycle_dir_t cyc;
+    if (parse_cycle_direction(desc, &cyc)) {
+        dst->monitor = ref->monitor;
+        dst->desktop = closest_desktop(ref->monitor, ref->desktop, cyc, sel);
+    } else if (streq("last", desc)) {
+        dst->monitor = mon;
+        dst->desktop = mon->last_desk;
+    } else if (streq("focused", desc)) {
+        dst->monitor = mon;
+        dst->desktop = mon->desk;
+    } else {
+        locate_desktop(desc, dst);
+    }
+
+    return (dst->desktop != NULL);
+}
+
+bool monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+    desktop_select_t sel;
+    sel = DESKTOP_ALL;
+    char *tok;
+    while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+        tok[0] = '\0';
+        tok++;
+        if (streq("free", tok)) {
+            sel = DESKTOP_FREE;
+        } else if (streq("occupied", tok)) {
+            sel = DESKTOP_OCCUPIED;
+        }
+    }
+
+    dst->monitor = NULL;
+
+    direction_t dir;
+    cycle_dir_t cyc;
+    if (parse_direction(desc, &dir)) {
+        dst->monitor = nearest_monitor(ref->monitor, dir);
+    } else if (parse_cycle_direction(desc, &cyc)) {
+        dst->monitor = closest_monitor(ref->monitor, cyc, sel);
+    } else if (streq("last", desc)) {
+        dst->monitor = last_mon;
+    } else if (streq("focused", desc)) {
+        dst->monitor = mon;
+    } else {
+        locate_monitor(desc, dst);
+    }
+
+    return (dst->monitor != NULL);
+}
+
+bool locate_window(xcb_window_t win, coordinates_t *loc)
+{
+    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+                if (n->client->window == win) {
+                    loc->monitor = m;
+                    loc->desktop = d;
+                    loc->node = n;
+                    return true;
+                }
+    return false;
+}
+
+bool locate_desktop(char *name, coordinates_t *loc)
+{
+    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+            if (streq(d->name, name)) {
+                loc->monitor = m;
+                loc->desktop = d;
+                return true;
+            }
+    return false;
+}
+
+bool locate_monitor(char *name, coordinates_t *loc)
+{
+    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+        if (streq(m->name, name)) {
+            loc->monitor = m;
+            return true;
+        }
+    return false;
+}
diff --git a/query.h b/query.h
new file mode 100644 (file)
index 0000000..61baf72
--- /dev/null
+++ b/query.h
@@ -0,0 +1,24 @@
+#ifndef _QUERY_H
+#define _QUERY_H
+
+typedef enum {
+    DOMAIN_MONITOR,
+    DOMAIN_DESKTOP,
+    DOMAIN_WINDOW,
+    DOMAIN_TREE,
+    DOMAIN_HISTORY
+} domain_t;
+
+void query_monitors(coordinates_t, domain_t, char *);
+void query_desktops(monitor_t *, domain_t, coordinates_t, unsigned int, char *);
+void query_tree(desktop_t *, node_t *, char *, unsigned int);
+void query_history(coordinates_t, char *);
+void query_windows(coordinates_t, char *);
+bool locate_window(xcb_window_t, coordinates_t *);
+bool locate_desktop(char *, coordinates_t *);
+bool locate_monitor(char *, coordinates_t *);
+bool node_from_desc(char *, coordinates_t *, coordinates_t *);
+bool desktop_from_desc(char *, coordinates_t *, coordinates_t *);
+bool monitor_from_desc(char *, coordinates_t *, coordinates_t *);
+
+#endif
diff --git a/restore.c b/restore.c
new file mode 100644 (file)
index 0000000..06afbd0
--- /dev/null
+++ b/restore.c
@@ -0,0 +1,182 @@
+#include <ctype.h>
+#include <string.h>
+#include "types.h"
+#include "tree.h"
+#include "settings.h"
+#include "ewmh.h"
+#include "bspwm.h"
+#include "query.h"
+#include "restore.h"
+
+void restore_tree(char *file_path)
+{
+    if (file_path == NULL)
+        return;
+
+    FILE *snapshot = fopen(file_path, "r");
+    if (snapshot == NULL) {
+        warn("Restore: can't open file\n");
+        return;
+    }
+
+    PUTS("restore tree");
+
+    char line[MAXLEN];
+    monitor_t *m = NULL;
+    desktop_t *d = NULL;
+    node_t *n = NULL;
+    num_clients = 0;
+    unsigned int level, last_level = 0;
+    bool aborted = false;
+
+    while (!aborted && fgets(line, sizeof(line), snapshot) != NULL) {
+        unsigned int len = strlen(line);
+        level = 0;
+        while (level < strlen(line) && isspace(line[level]))
+            level++;
+        if (level == 0) {
+            if (m == NULL)
+                m = mon_head;
+            else
+                m = m->next;
+            if (len >= 2)
+                switch (line[len - 2]) {
+                    case '#':
+                        mon = m;
+                        break;
+                    case '~':
+                        last_mon = m;
+                        break;
+                }
+        } else if (level == 2) {
+            if (d == NULL)
+                d = m->desk_head;
+            else
+                d = d->next;
+            int i = len - 1;
+            while (i > 0 && !isupper(line[i]))
+                i--;
+            if (line[i] == 'M')
+                d->layout = LAYOUT_MONOCLE;
+            else if (line[i] == 'T')
+                d->layout = LAYOUT_TILED;
+            if (len >= 2)
+                switch (line[len - 2]) {
+                    case '@':
+                        m->desk = d;
+                        break;
+                    case '~':
+                        m->last_desk = d;
+                        break;
+                }
+        } else {
+            node_t *birth = make_node();
+            if (level == 4) {
+                empty_desktop(d);
+                d->root = birth;
+            } else {
+                if (level > last_level) {
+                    n->first_child = birth;
+                } else {
+                    do {
+                        n = n->parent;
+                    } while (n != NULL && n->second_child != NULL);
+                    if (n == NULL) {
+                        warn("Restore: file is malformed\n");
+                        aborted = true;
+                    }
+                    n->second_child = birth;
+                }
+                birth->parent = n;
+            }
+            n = birth;
+
+            char br;
+            if (isupper(line[level])) {
+                char st;
+                sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
+                if (st == 'H')
+                    n->split_type = TYPE_HORIZONTAL;
+                else if (st == 'V')
+                    n->split_type = TYPE_VERTICAL;
+            } else {
+                client_t *c = make_client(XCB_NONE);
+                num_clients++;
+                char floating, transient, fullscreen, urgent, locked;
+                sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c%c%c%c%c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &floating, &transient, &fullscreen, &urgent, &locked);
+                c->floating = (floating == '-' ? false : true);
+                c->transient = (transient == '-' ? false : true);
+                c->fullscreen = (fullscreen == '-' ? false : true);
+                c->urgent = (urgent == '-' ? false : true);
+                c->locked = (locked == '-' ? false : true);
+                n->client = c;
+                if (len >= 2 && line[len - 2] == '*')
+                    d->focus = n;
+            }
+            if (br == 'a')
+                n->birth_rotation = 90;
+            else if (br == 'c')
+                n->birth_rotation = 270;
+            else if (br == 'm')
+                n->birth_rotation = 0;
+        }
+        last_level = level;
+    }
+
+    fclose(snapshot);
+
+    if (!aborted) {
+        for (monitor_t *m = mon_head; m != NULL; m = m->next)
+            for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
+                for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                    uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)};
+                    xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
+                    if (n->client->floating) {
+                        n->vacant = true;
+                        update_vacant_state(n->parent);
+                    }
+                }
+        ewmh_update_current_desktop();
+    }
+}
+
+void restore_history(char *file_path)
+{
+    if (file_path == NULL)
+        return;
+
+    FILE *snapshot = fopen(file_path, "r");
+    if (snapshot == NULL) {
+        warn("Restore history: can't open file\n");
+        return;
+    }
+
+    PUTS("restore history");
+
+    char line[MAXLEN];
+    desktop_t *d = NULL;
+    unsigned int level;
+
+    while (fgets(line, sizeof(line), snapshot) != NULL) {
+        unsigned int i = strlen(line) - 1;
+        while (i > 0 && isspace(line[i]))
+            line[i--] = '\0';
+        level = 0;
+        while (level < strlen(line) && isspace(line[level]))
+            level++;
+        if (level == 0) {
+            coordinates_t loc;
+            if (locate_desktop(line + level, &loc))
+                d = loc.desktop;
+        } else if (d != NULL) {
+            xcb_window_t win;
+            if (sscanf(line + level, "%X", &win) == 1) {
+                coordinates_t loc;
+                if (locate_window(win, &loc))
+                    history_add(d->history, loc.node);
+            }
+        }
+    }
+
+    fclose(snapshot);
+}
diff --git a/restore.h b/restore.h
new file mode 100644 (file)
index 0000000..d95559f
--- /dev/null
+++ b/restore.h
@@ -0,0 +1,7 @@
+#ifndef _RESTORE_H
+#define _RESTORE_H
+
+void restore_tree(char *);
+void restore_history(char *);
+
+#endif
diff --git a/rules.c b/rules.c
index 9845bac0b2fa1bcaf8bd303f04042371c27445d6..05b9cda402109cd36ed404fc6131a85853b8c87b 100644 (file)
--- a/rules.c
+++ b/rules.c
@@ -6,6 +6,7 @@
 #include "bspwm.h"
 #include "ewmh.h"
 #include "rules.h"
+#include "query.h"
 
 void add_rule(rule_t *r)
 {
@@ -40,17 +41,6 @@ void remove_rule_by_uid(unsigned int uid)
     remove_rule(find_rule(uid));
 }
 
-void prune_rules(desktop_t *d)
-{
-    rule_t *r = rule_head;
-    while (r != NULL) {
-        rule_t *next = r->next;
-        if (r->effect.desktop == d)
-            remove_rule(r);
-        r = next;
-    }
-}
-
 rule_t *find_rule(unsigned int uid)
 {
     for (rule_t *r = rule_head; r != NULL; r = r->next)
@@ -63,8 +53,8 @@ bool is_match(rule_t *r, xcb_window_t win)
 {
     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
-            && (strcmp(reply.class_name, r->cause.name) == 0
-                || strcmp(reply.instance_name, r->cause.name) == 0)) {
+            && (streq(reply.class_name, r->cause.name)
+                || streq(reply.instance_name, r->cause.name))) {
         xcb_icccm_get_wm_class_reply_wipe(&reply);
         return true;
     }
@@ -126,21 +116,36 @@ void handle_rules(xcb_window_t win, monitor_t **m, desktop_t **d, bool *floating
                 *floating = true;
             if (efc.follow)
                 *follow = true;
-            if (efc.monitor != NULL && efc.desktop != NULL) {
-                *m = efc.monitor;
-                *d = efc.desktop;
+            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;
+                }
             }
         }
         rule = rule->next;
     }
 }
 
-void list_rules(char *rsp)
+void list_rules(char *pattern, char *rsp)
 {
     char line[MAXLEN];
 
     for (rule_t *r = rule_head; r != NULL; r = r->next) {
-        snprintf(line, sizeof(line), "%2X %s %s %s %s\n", r->uid, r->cause.name, (r->effect.desktop != NULL ? r->effect.desktop->name : "\b"), (r->effect.floating ? "floating" : "\b"), (r->effect.follow ? "follow" : "\b"));
+        if (pattern != NULL && !streq(pattern, r->cause.name))
+            continue;
+        snprintf(line, sizeof(line), "%2X %s", r->uid, r->cause.name);
         strncat(rsp, line, REMLEN(rsp));
+        if (r->effect.floating)
+            strncat(rsp, " --floating", REMLEN(rsp));
+        if (r->effect.follow)
+            strncat(rsp, " --follow", 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/rules.h b/rules.h
index f057d4fc335743953a8d983b1683cca9793b5677..9042c4870cc5168e9b36b40884926c7e98fc0d18 100644 (file)
--- a/rules.h
+++ b/rules.h
@@ -8,6 +8,6 @@ void prune_rules(desktop_t *);
 rule_t *find_rule(unsigned int);
 bool is_match(rule_t *, xcb_window_t);
 void handle_rules(xcb_window_t, monitor_t **, desktop_t **, bool *, bool *, bool *, bool *, bool *, bool *);
-void list_rules(char *);
+void list_rules(char *, char *);
 
 #endif
index 088baa9c46165f2d67e1bb072ff5ea716edd7435..aa5e221b22f9e8edc51e75e592261d93af5fd286 100644 (file)
@@ -45,14 +45,14 @@ void load_settings(void)
     strncpy(normal_locked_border_color, NORMAL_LOCKED_BORDER_COLOR, sizeof(normal_locked_border_color));
     strncpy(urgent_border_color, URGENT_BORDER_COLOR, sizeof(urgent_border_color));
 
-    normal_border_color_pxl = get_color(normal_border_color);
-    focused_border_color_pxl = get_color(active_border_color);
-    active_border_color_pxl = get_color(active_border_color);
-    presel_border_color_pxl = get_color(presel_border_color);
-    focused_locked_border_color_pxl = get_color(active_locked_border_color);
-    active_locked_border_color_pxl = get_color(active_locked_border_color);
-    normal_locked_border_color_pxl = get_color(normal_locked_border_color);
-    urgent_border_color_pxl = get_color(urgent_border_color);
+    get_color(normal_border_color, &normal_border_color_pxl);
+    get_color(active_border_color, &focused_border_color_pxl);
+    get_color(active_border_color, &active_border_color_pxl);
+    get_color(presel_border_color, &presel_border_color_pxl);
+    get_color(active_locked_border_color, &focused_locked_border_color_pxl);
+    get_color(active_locked_border_color, &active_locked_border_color_pxl);
+    get_color(normal_locked_border_color, &normal_locked_border_color_pxl);
+    get_color(urgent_border_color, &urgent_border_color_pxl);
 
     strncpy(wm_name, WM_NAME, sizeof(wm_name));
 
diff --git a/tree.c b/tree.c
index 3416d78600cf4cdb36a0de15467294b0be31cbf8..e99c91d48321de58e04894d155c3f5299a6be397 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,12 +1,7 @@
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
-#include <limits.h>
 #include <math.h>
+#include <limits.h>
 #include <float.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_event.h>
 #include "settings.h"
 #include "helpers.h"
 #include "window.h"
@@ -46,7 +41,8 @@ bool is_second_child(node_t *n)
 
 void change_split_ratio(node_t *n, value_change_t chg)
 {
-    n->split_ratio = pow(n->split_ratio, (chg == CHANGE_INCREASE ? INC_EXP : DEC_EXP));
+    n->split_ratio = pow(n->split_ratio,
+            (chg == CHANGE_INCREASE ? (1 / GROWTH_FACTOR) : GROWTH_FACTOR));
 }
 
 void change_layout(monitor_t *m, desktop_t *d, layout_t l)
@@ -57,15 +53,15 @@ void change_layout(monitor_t *m, desktop_t *d, layout_t l)
         put_status();
 }
 
-void reset_mode(desktop_t *d, node_t *n, cancel_option_t c)
+void reset_mode(coordinates_t *loc)
 {
-    if (c == CANCEL_OPTION_FOCUSED) {
-        n->split_mode = MODE_AUTOMATIC;
-        window_draw_border(mon->desk->focus, d->focus == n, true);
-    } else if (c == CANCEL_OPTION_ALL) {
-        for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
+    if (loc->node != NULL) {
+        loc->node->split_mode = MODE_AUTOMATIC;
+        window_draw_border(loc->node, loc->desktop->focus == loc->node, mon == loc->monitor);
+    } else if (loc->desktop != NULL) {
+        for (node_t *a = first_extrema(loc->desktop->root); a != NULL; a = next_leaf(a, loc->desktop->root)) {
             a->split_mode = MODE_AUTOMATIC;
-            window_draw_border(a, d->focus == a, true);
+            window_draw_border(a, loc->desktop->focus == a, mon == loc->monitor);
         }
     }
 }
@@ -114,17 +110,37 @@ node_t *prev_leaf(node_t *n, node_t *r)
     return second_extrema(p->parent->first_child);
 }
 
-bool is_adjacent(node_t *a, node_t *r)
+/* bool is_adjacent(node_t *a, node_t *r) */
+/* { */
+/*     node_t *f = r->parent; */
+/*     node_t *p = a; */
+/*     bool first_child = is_first_child(r); */
+/*     while (p != r) { */
+/*         if (p->parent->split_type == f->split_type && is_first_child(p) == first_child) */
+/*             return false; */
+/*         p = p->parent; */
+/*     } */
+/*     return true; */
+/* } */
+
+/* Returns true if *b* is adjacent to *a* in the direction *dir* */
+bool is_adjacent(node_t *a, node_t *b, direction_t dir)
 {
-    node_t *f = r->parent;
-    node_t *p = a;
-    bool first_child = is_first_child(r);
-    while (p != r) {
-        if (p->parent->split_type == f->split_type && is_first_child(p) == first_child)
-            return false;
-        p = p->parent;
+    switch (dir) {
+        case DIR_RIGHT:
+            return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
+            break;
+        case DIR_DOWN:
+            return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
+            break;
+        case DIR_LEFT:
+            return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
+            break;
+        case DIR_UP:
+            return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
+            break;
     }
-    return true;
+    return false;
 }
 
 node_t *find_fence(node_t *n, direction_t dir)
@@ -205,7 +221,7 @@ node_t *nearest_from_history(focus_history_t *f, node_t *n, direction_t dir)
     int min_rank = INT_MAX;
 
     for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
-        if (!is_tiled(a->client) || !is_adjacent(a, target) || a == n)
+        if (a->vacant || !is_adjacent(n, a, dir) || a == n)
             continue;
         int rank = history_rank(f, a);
         if (rank >= 0 && rank < min_rank) {
@@ -246,7 +262,7 @@ node_t *nearest_from_distance(desktop_t *d, node_t *n, direction_t dir)
 
     for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
         if (is_tiled(a->client) != is_tiled(n->client)
-                || (is_tiled(a->client) && !is_adjacent(a, target))
+                || (is_tiled(a->client) && !is_adjacent(n, a, dir))
                 || a == n)
             continue;
         get_side_handle(a->client, dir2, &pt2);
@@ -324,23 +340,23 @@ void move_fence(node_t *n, direction_t dir, fence_move_t mov)
         change_split_ratio(fence, CHANGE_DECREASE);
 }
 
-void rotate_tree(node_t *n, rotate_t rot)
+void rotate_tree(node_t *n, int rot)
 {
-    if (n == NULL || is_leaf(n) || rot == ROTATE_IDENTITY)
+    if (n == NULL || is_leaf(n) || rot == 0)
         return;
 
     node_t *tmp;
 
-    if ((rot == ROTATE_CLOCKWISE && n->split_type == TYPE_HORIZONTAL)
-            || (rot == ROTATE_COUNTER_CLOCKWISE && n->split_type == TYPE_VERTICAL)
-            || rot == ROTATE_FULL_CYCLE) {
+    if ((rot == 90 && n->split_type == TYPE_HORIZONTAL)
+            || (rot == 270 && n->split_type == TYPE_VERTICAL)
+            || rot == 180) {
         tmp = n->first_child;
         n->first_child = n->second_child;
         n->second_child = tmp;
         n->split_ratio = 1.0 - n->split_ratio;
     }
 
-    if (rot != ROTATE_FULL_CYCLE) {
+    if (rot != 180) {
         if (n->split_type == TYPE_HORIZONTAL)
             n->split_type = TYPE_VERTICAL;
         else if (n->split_type == TYPE_VERTICAL)
@@ -361,19 +377,11 @@ void rotate_brother(node_t *n)
         rotate_tree(n->parent->first_child, n->birth_rotation);
 }
 
-void unrotate_tree(node_t *n, rotate_t rot)
+void unrotate_tree(node_t *n, int rot)
 {
-    switch(rot) {
-        case ROTATE_CLOCKWISE:
-            rotate_tree(n, ROTATE_COUNTER_CLOCKWISE);
-            break;
-        case ROTATE_COUNTER_CLOCKWISE:
-            rotate_tree(n, ROTATE_CLOCKWISE);
-            break;
-        case ROTATE_IDENTITY:
-        case ROTATE_FULL_CYCLE:
-            break;
-    }
+    if (rot == 0)
+        return;
+    rotate_tree(n, 360 - rot);
 }
 
 void unrotate_brother(node_t *n)
@@ -405,20 +413,6 @@ void flip_tree(node_t *n, flip_t flp)
     flip_tree(n->second_child, flp);
 }
 
-void list_history(char *rsp)
-{
-    char line[MAXLEN];
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-            snprintf(line, sizeof(line), "%s\n", d->name);
-            strncat(rsp, line, REMLEN(rsp));
-            for (node_list_t *a = d->history->tail; a != NULL; a = a->prev) {
-                snprintf(line, sizeof(line), "  %X\n", a->node->client->window);
-                strncat(rsp, line, REMLEN(rsp));
-            }
-        }
-}
-
 int balance_tree(node_t *n)
 {
     if (n == NULL || n->vacant) {
@@ -510,7 +504,6 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
                 fence = rect.width * n->split_ratio;
                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
                 second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
-
             } else if (n->split_type == TYPE_HORIZONTAL) {
                 fence = rect.height * n->split_ratio;
                 first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
@@ -568,15 +561,15 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
                     c->split_type = p->split_type;
                     c->split_ratio = p->split_ratio;
                     p->parent = c;
-                    rotate_t rot;
+                    int rot;
                     if (is_first_child(f)) {
                         c->first_child = n;
                         c->second_child = p;
-                        rot = ROTATE_CLOCKWISE;
+                        rot = 90;
                     } else {
                         c->first_child = p;
                         c->second_child = n;
-                        rot = ROTATE_COUNTER_CLOCKWISE;
+                        rot = 270;
                     }
                     if (!is_floating(n->client))
                         rotate_tree(p, rot);
@@ -593,7 +586,7 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
                 c->split_ratio = f->split_ratio;
                 c->parent = p;
                 f->parent = c;
-                f->birth_rotation = ROTATE_IDENTITY;
+                f->birth_rotation = 0;
                 switch (f->split_dir) {
                     case DIR_LEFT:
                         c->split_type = TYPE_VERTICAL;
@@ -786,8 +779,8 @@ void swap_nodes(node_t *n1, node_t *n2, bool interpret)
     node_t *pn2 = n2->parent;
     bool n1_first_child = is_first_child(n1);
     bool n2_first_child = is_first_child(n2);
-    rotate_t br1 = n1->birth_rotation;
-    rotate_t br2 = n2->birth_rotation;
+    int br1 = n1->birth_rotation;
+    int br2 = n2->birth_rotation;
 
     if (pn1 != NULL) {
         if (n1_first_child)
@@ -888,15 +881,15 @@ void select_monitor(monitor_t *m)
     put_status();
 }
 
-monitor_t *nearest_monitor(direction_t dir)
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir)
 {
     int dmin = INT_MAX;
     monitor_t *nearest = NULL;
-    xcb_rectangle_t rect = mon->rectangle;
-    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-        if (m == mon)
+    xcb_rectangle_t rect = m->rectangle;
+    for (monitor_t *f = mon_head; f != NULL; f = f->next) {
+        if (f == m)
             continue;
-        xcb_rectangle_t r = m->rectangle;
+        xcb_rectangle_t r = f->rectangle;
         if ((dir == DIR_LEFT && r.x < rect.x) ||
                 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
                 (dir == DIR_UP && r.y < rect.y) ||
@@ -905,7 +898,7 @@ monitor_t *nearest_monitor(direction_t dir)
                 ABS((r.y + r.height / 2) - (rect.y + rect.height / 2));
             if (d < dmin) {
                 dmin = d;
-                nearest = m;
+                nearest = f;
             }
         }
     }
@@ -931,41 +924,50 @@ void select_desktop(monitor_t *m, desktop_t *d)
     put_status();
 }
 
-void cycle_monitor(cycle_dir_t dir)
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
 {
-    monitor_t *m = NULL;
-    if (dir == CYCLE_NEXT)
-        m = (mon->next == NULL ? mon_head : mon->next);
-    else if (dir == CYCLE_PREV)
-        m = (mon->prev == NULL ? mon_tail : mon->prev);
-    focus_node(m, m->desk, m->desk->focus);
+    monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
+    if (f == NULL)
+        f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+
+    while (f != m) {
+        if (sel == DESKTOP_ALL
+                || (sel == DESKTOP_FREE && f->desk->root == NULL)
+                || (sel == DESKTOP_OCCUPIED && f->desk->root != NULL)) {
+            return f;
+        }
+        f = (dir == CYCLE_PREV ? m->prev : m->next);
+        if (f == NULL)
+            f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+    }
+
+    return NULL;
 }
 
-void cycle_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, skip_desktop_t skip)
+desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel)
 {
     desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
     if (f == NULL)
         f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
 
     while (f != d) {
-        if (skip == DESKTOP_SKIP_NONE
-                || (skip == DESKTOP_SKIP_FREE && f->root != NULL)
-                || (skip == DESKTOP_SKIP_OCCUPIED && f->root == NULL)) {
-            focus_node(m, f, f->focus);
-            return;
+        if (sel == DESKTOP_ALL
+                || (sel == DESKTOP_FREE && f->root == NULL)
+                || (sel == DESKTOP_OCCUPIED && f->root != NULL)) {
+            return f;
         }
         f = (dir == CYCLE_PREV ? f->prev : f->next);
         if (f == NULL)
             f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
     }
+
+    return NULL;
 }
 
-void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_client_t skip)
+node_t *closest_node(desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
 {
     if (n == NULL)
-        return;
-
-    PUTS("cycle leaf");
+        return NULL;
 
     node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
     if (f == NULL)
@@ -973,40 +975,21 @@ void cycle_leaf(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, skip_cli
 
     while (f != n) {
         bool tiled = is_tiled(f->client);
-        if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !tiled) || (skip == CLIENT_SKIP_FLOATING && tiled)
-                || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
-                || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0)) {
-            focus_node(m, d, f);
-            return;
+        if ((sel.type == CLIENT_TYPE_ALL
+                    || (tiled && sel.type == CLIENT_TYPE_TILED)
+                    || (!tiled && sel.type == CLIENT_TYPE_FLOATING)) &&
+                (sel.class == CLIENT_CLASS_ALL
+                 || (sel.class == CLIENT_CLASS_EQUAL
+                     && streq(f->client->class_name, n->client->class_name))
+                 || (sel.class == CLIENT_CLASS_DIFFER
+                     && !streq(f->client->class_name, n->client->class_name)))) {
+            return f;
         }
         f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
         if (f == NULL)
             f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
     }
-}
-
-void nearest_leaf(monitor_t *m, desktop_t *d, node_t *n, nearest_arg_t dir, skip_client_t skip)
-{
-    if (n == NULL)
-        return;
-
-    PUTS("nearest leaf");
-
-    node_t *x = NULL;
-
-    for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root))
-        if (skip == CLIENT_SKIP_NONE || (skip == CLIENT_SKIP_TILED && !is_tiled(f->client)) || (skip == CLIENT_SKIP_FLOATING && is_tiled(f->client))
-                || (skip == CLIENT_SKIP_CLASS_DIFFER && strcmp(f->client->class_name, n->client->class_name) == 0)
-                || (skip == CLIENT_SKIP_CLASS_EQUAL && strcmp(f->client->class_name, n->client->class_name) != 0))
-            if ((dir == NEAREST_OLDER
-                        && (f->client->uid < n->client->uid)
-                        && (x == NULL || f->client->uid > x->client->uid))
-                    || (dir == NEAREST_NEWER
-                        && (f->client->uid > n->client->uid)
-                        && (x == NULL || f->client->uid < x->client->uid)))
-                x = f;
-
-    focus_node(m, d, x);
+    return NULL;
 }
 
 void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
@@ -1057,264 +1040,3 @@ void fit_monitor(monitor_t *m, client_t *c)
         crect.y -= mrect.height;
     c->floating_rectangle = crect;
 }
-
-void put_status(void)
-{
-    if (status_fifo == NULL)
-        return;
-    if (status_prefix != NULL)
-        fprintf(status_fifo, "%s", status_prefix);
-    bool urgent = false;
-    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-        fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
-            for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
-                urgent |= n->client->urgent;
-            fprintf(status_fifo, "%c%s:", m->desk == d ? (urgent ? 'U' : 'D') : (d->root == NULL ? 'E' : (urgent ? 'u' : 'd')), d->name);
-        }
-    }
-    if (mon != NULL && mon->desk != NULL)
-        fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
-    fprintf(status_fifo, "\n");
-    fflush(status_fifo);
-}
-
-void list_monitors(list_option_t opt, char *rsp)
-{
-    char line[MAXLEN];
-    for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-        snprintf(line, sizeof(line), "%s %ux%u%+i%+i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y);
-        strncat(rsp, line, REMLEN(rsp));
-        if (m == mon)
-            strncat(rsp, " #\n", REMLEN(rsp));
-        else if (m == last_mon)
-            strncat(rsp, " ~\n", REMLEN(rsp));
-        else
-            strncat(rsp, "\n", REMLEN(rsp));
-        if (opt == LIST_OPTION_VERBOSE)
-            list_desktops(m, opt, 1, rsp);
-    }
-}
-
-void list_desktops(monitor_t *m, list_option_t opt, unsigned int depth, char *rsp)
-{
-    char line[MAXLEN];
-    for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-        for (unsigned int i = 0; i < depth; i++)
-            strncat(rsp, "  ", REMLEN(rsp));
-        snprintf(line, sizeof(line), "%s %c", d->name, (d->layout == LAYOUT_TILED ? 'T' : 'M'));
-        strncat(rsp, line, REMLEN(rsp));
-        if (d == m->desk)
-            strncat(rsp, " @\n", REMLEN(rsp));
-        else if (d == m->last_desk)
-            strncat(rsp, " ~\n", REMLEN(rsp));
-        else
-            strncat(rsp, "\n", REMLEN(rsp));
-        if (opt == LIST_OPTION_VERBOSE)
-            list(d, d->root, rsp, depth + 1);
-    }
-}
-
-void list(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
-{
-    if (n == NULL)
-        return;
-
-    char line[MAXLEN];
-
-    for (unsigned int i = 0; i < depth; i++)
-        strncat(rsp, "  ", REMLEN(rsp));
-
-    if (is_leaf(n)) {
-        client_t *c = n->client;
-        snprintf(line, sizeof(line), "%c %s %X %u %u %ux%u%+i%+i %c%c%c%c%c", (n->birth_rotation == ROTATE_CLOCKWISE ? 'a' : (n->birth_rotation == ROTATE_COUNTER_CLOCKWISE ? 'c' : 'm')), c->class_name, c->window, c->uid, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'));
-    } else {
-        snprintf(line, sizeof(line), "%c %c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == ROTATE_CLOCKWISE ? 'a' : (n->birth_rotation == ROTATE_COUNTER_CLOCKWISE ? 'c' : 'm')), n->split_ratio);
-    }
-
-    strncat(rsp, line, REMLEN(rsp));
-
-    if (n == d->focus)
-        strncat(rsp, " *\n", REMLEN(rsp));
-    else
-        strncat(rsp, "\n", REMLEN(rsp));
-
-    list(d, n->first_child, rsp, depth + 1);
-    list(d, n->second_child, rsp, depth + 1);
-}
-
-void restore_layout(char *file_path)
-{
-    if (file_path == NULL)
-        return;
-
-    FILE *snapshot = fopen(file_path, "r");
-    if (snapshot == NULL) {
-        warn("restore: can't open file\n");
-        return;
-    }
-
-    PUTS("restore layout");
-
-    char line[MAXLEN];
-    monitor_t *m = NULL;
-    desktop_t *d = NULL;
-    node_t *n = NULL;
-    num_clients = 0;
-    unsigned int level, last_level = 0, max_uid = 0;
-    bool aborted = false;
-
-    while (!aborted && fgets(line, sizeof(line), snapshot) != NULL) {
-        unsigned int len = strlen(line);
-        level = 0;
-        while (level < strlen(line) && isspace(line[level]))
-            level++;
-        if (level == 0) {
-            if (m == NULL)
-                m = mon_head;
-            else
-                m = m->next;
-            if (len >= 2)
-                switch (line[len - 2]) {
-                    case '#':
-                        mon = m;
-                        break;
-                    case '~':
-                        last_mon = m;
-                        break;
-                }
-        } else if (level == 2) {
-            if (d == NULL)
-                d = m->desk_head;
-            else
-                d = d->next;
-            int i = len - 1;
-            while (i > 0 && !isupper(line[i]))
-                i--;
-            if (line[i] == 'M')
-                d->layout = LAYOUT_MONOCLE;
-            else if (line[i] == 'T')
-                d->layout = LAYOUT_TILED;
-            if (len >= 2)
-                switch (line[len - 2]) {
-                    case '@':
-                        m->desk = d;
-                        break;
-                    case '~':
-                        m->last_desk = d;
-                        break;
-                }
-        } else {
-            node_t *birth = make_node();
-            if (level == 4) {
-                empty_desktop(d);
-                d->root = birth;
-            } else {
-                if (level > last_level) {
-                    n->first_child = birth;
-                } else {
-                    do {
-                        n = n->parent;
-                    } while (n != NULL && n->second_child != NULL);
-                    if (n == NULL) {
-                        warn("restore: file is malformed\n");
-                        aborted = true;
-                    }
-                    n->second_child = birth;
-                }
-                birth->parent = n;
-            }
-            n = birth;
-
-            char br;
-            if (isupper(line[level])) {
-                char st;
-                sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
-                if (st == 'H')
-                    n->split_type = TYPE_HORIZONTAL;
-                else if (st == 'V')
-                    n->split_type = TYPE_VERTICAL;
-            } else {
-                client_t *c = make_client(XCB_NONE);
-                num_clients++;
-                char floating, transient, fullscreen, urgent, locked;
-                sscanf(line + level, "%c %s %X %u %u %hux%hu%hi%hi %c%c%c%c%c", &br, c->class_name, &c->window, &c->uid, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &floating, &transient, &fullscreen, &urgent, &locked);
-                c->floating = (floating == '-' ? false : true);
-                c->transient = (transient == '-' ? false : true);
-                c->fullscreen = (fullscreen == '-' ? false : true);
-                c->urgent = (urgent == '-' ? false : true);
-                c->locked = (locked == '-' ? false : true);
-                if (c->uid > max_uid)
-                    max_uid = c->uid;
-                n->client = c;
-                if (len >= 2 && line[len - 2] == '*')
-                    d->focus = n;
-            }
-            if (br == 'a')
-                n->birth_rotation = ROTATE_CLOCKWISE;
-            else if (br == 'c')
-                n->birth_rotation = ROTATE_COUNTER_CLOCKWISE;
-            else if (br == 'm')
-                n->birth_rotation = ROTATE_IDENTITY;
-        }
-        last_level = level;
-    }
-
-    fclose(snapshot);
-
-    if (!aborted) {
-        client_uid = max_uid + 1;
-        for (monitor_t *m = mon_head; m != NULL; m = m->next)
-            for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-                for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                    uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)};
-                    xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
-                    if (n->client->floating) {
-                        n->vacant = true;
-                        update_vacant_state(n->parent);
-                    }
-                }
-        ewmh_update_current_desktop();
-    }
-}
-
-void restore_history(char *file_path)
-{
-    if (file_path == NULL)
-        return;
-
-    FILE *snapshot = fopen(file_path, "r");
-    if (snapshot == NULL) {
-        warn("restore history: can't open file\n");
-        return;
-    }
-
-    PUTS("restore history");
-
-    char line[MAXLEN];
-    desktop_t *d = NULL;
-    unsigned int level;
-
-    while (fgets(line, sizeof(line), snapshot) != NULL) {
-        unsigned int i = strlen(line) - 1;
-        while (i > 0 && isspace(line[i]))
-            line[i--] = '\0';
-        level = 0;
-        while (level < strlen(line) && isspace(line[level]))
-            level++;
-        if (level == 0) {
-            desktop_location_t loc;
-            if (locate_desktop(line + level, &loc))
-                d = loc.desktop;
-        } else if (d != NULL) {
-            xcb_window_t win;
-            if (sscanf(line + level, "%X", &win) == 1) {
-                window_location_t loc;
-                if (locate_window(win, &loc))
-                    history_add(d->history, loc.node);
-            }
-        }
-    }
-
-    fclose(snapshot);
-}
diff --git a/tree.h b/tree.h
index afcea9530f8244fa53aa833d76dbc3648c1e8310..05dc9c8bc3f8f3d84c45eb0a6b24c8e6785b82d0 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -1,9 +1,23 @@
 #ifndef _TREE_H
 #define _TREE_H
 
-#define INC_EXP 0.9
-#define DEC_EXP 1.1
+#define GROWTH_FACTOR  1.1
 
+void arrange(monitor_t *, desktop_t *);
+void apply_layout(monitor_t *, desktop_t *, node_t *, xcb_rectangle_t, xcb_rectangle_t);
+void focus_node(monitor_t *, desktop_t *, node_t *);
+void insert_node(monitor_t *, desktop_t *, node_t *, node_t *);
+void unlink_node(desktop_t *, node_t *);
+void remove_node(desktop_t *, node_t *);
+void swap_nodes(node_t *, node_t *, bool);
+void pseudo_focus(desktop_t *, node_t *);
+void update_current(void);
+node_t *find_fence(node_t *, direction_t);
+node_t *nearest_neighbor(desktop_t *, node_t *, direction_t);
+node_t *nearest_from_tree(node_t *, direction_t);
+node_t *nearest_from_distance(desktop_t *, node_t *, direction_t);
+node_t *nearest_from_history(focus_history_t *, node_t *, direction_t);
+node_t *find_biggest(desktop_t *);
 bool is_leaf(node_t *);
 bool is_tiled(client_t *);
 bool is_floating(client_t *);
@@ -11,55 +25,32 @@ bool is_first_child(node_t *);
 bool is_second_child(node_t *);
 void change_split_ratio(node_t *, value_change_t);
 void change_layout(monitor_t *, desktop_t *, layout_t);
-void reset_mode(desktop_t *, node_t *, cancel_option_t);
+void reset_mode(coordinates_t *);
 node_t *first_extrema(node_t *);
 node_t *second_extrema(node_t *);
 node_t *next_leaf(node_t *, node_t *);
 node_t *prev_leaf(node_t *, node_t *);
-bool is_adjacent(node_t *, node_t *);
-node_t *find_fence(node_t *, direction_t);
-node_t *nearest_neighbor(desktop_t *, node_t *, direction_t);
-node_t *nearest_from_tree(node_t *, direction_t);
-node_t *nearest_from_distance(desktop_t *, node_t *, direction_t);
-node_t *nearest_from_history(focus_history_t *, node_t *, direction_t);
+bool is_adjacent(node_t *, node_t *, direction_t);
 void get_opposite(direction_t, direction_t *);
 int tiled_area(node_t *);
-node_t *find_biggest(desktop_t *);
 void move_fence(node_t *, direction_t, fence_move_t);
-void rotate_tree(node_t *, rotate_t);
+void rotate_tree(node_t *, int);
 void rotate_brother(node_t *);
-void unrotate_tree(node_t *, rotate_t);
+void unrotate_tree(node_t *, int);
 void unrotate_brother(node_t *);
 void flip_tree(node_t *, flip_t);
 int balance_tree(node_t *);
-void arrange(monitor_t *, desktop_t *);
-void apply_layout(monitor_t *, desktop_t *, node_t *, xcb_rectangle_t, xcb_rectangle_t);
-void insert_node(monitor_t *, desktop_t *, node_t *, node_t *);
-void pseudo_focus(desktop_t *, node_t *);
-void focus_node(monitor_t *, desktop_t *, node_t *);
-void update_current(void);
-void unlink_node(desktop_t *, node_t *);
-void remove_node(desktop_t *, node_t *);
 void destroy_tree(node_t *);
-void swap_nodes(node_t *, node_t *, bool);
 void fit_monitor(monitor_t *, client_t *);
 void transfer_node(monitor_t *, desktop_t *, monitor_t *, desktop_t *, node_t *);
 void transplant_node(monitor_t *, desktop_t *, node_t *, node_t *);
 void select_monitor(monitor_t *);
-monitor_t *nearest_monitor(direction_t);
 void select_desktop(monitor_t *, desktop_t *);
-void cycle_monitor(cycle_dir_t);
-void cycle_desktop(monitor_t *, desktop_t *, cycle_dir_t, skip_desktop_t);
-void cycle_leaf(monitor_t *, desktop_t *, node_t *, cycle_dir_t, skip_client_t);
-void nearest_leaf(monitor_t *, desktop_t *, node_t *, nearest_arg_t, skip_client_t);
+monitor_t *nearest_monitor(monitor_t *, direction_t);
+node_t *closest_node(desktop_t *, node_t *, cycle_dir_t, client_select_t);
+desktop_t *closest_desktop(monitor_t *, desktop_t *, cycle_dir_t, desktop_select_t);
+monitor_t *closest_monitor(monitor_t *, cycle_dir_t, desktop_select_t);
 void circulate_leaves(monitor_t *, desktop_t *, circulate_dir_t);
 void update_vacant_state(node_t *);
-void put_status(void);
-void list_history(char *);
-void list_monitors(list_option_t, char *);
-void list_desktops(monitor_t *, list_option_t, unsigned int, char *);
-void list(desktop_t *, node_t *, char *, unsigned int);
-void restore_layout(char *);
-void restore_history(char *);
 
 #endif
diff --git a/types.c b/types.c
index 6ebad6d7e39770002b462402dd087e63736f198c..fbdbae57f10632395435f8ddd9d495d241ac5df6 100644 (file)
--- a/types.c
+++ b/types.c
@@ -17,7 +17,7 @@ node_t *make_node(void)
     n->split_ratio = split_ratio;
     n->split_mode = MODE_AUTOMATIC;
     n->split_type = TYPE_VERTICAL;
-    n->birth_rotation = ROTATE_IDENTITY;
+    n->birth_rotation = 0;
     n->client = NULL;
     n->vacant = false;
     return n;
@@ -41,7 +41,7 @@ monitor_t *make_monitor(xcb_rectangle_t *rect)
 monitor_t *find_monitor(char *name)
 {
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        if (strcmp(m->name, name) == 0)
+        if (streq(m->name, name))
             return m;
     return NULL;
 }
@@ -102,6 +102,8 @@ void remove_monitor(monitor_t *m)
 
 void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
 {
+    if (ms == md)
+        return;
     desktop_t *dd = ms->desk;
     unlink_desktop(ms, d);
     insert_desktop(md, d);
@@ -201,7 +203,6 @@ void remove_desktop(monitor_t *m, desktop_t *d)
 {
     PRINTF("remove desktop %s\n", d->name);
 
-    prune_rules(d);
     unlink_desktop(m, d);
     empty_desktop(d);
     free(d);
@@ -215,7 +216,6 @@ client_t *make_client(xcb_window_t win)
 {
     client_t *c = malloc(sizeof(client_t));
     strncpy(c->class_name, MISSING_VALUE, sizeof(c->class_name));
-    c->uid = ++client_uid;
     c->border_width = border_width;
     c->window = win;
     c->floating = c->transient = c->fullscreen = c->locked = c->urgent = false;
@@ -228,8 +228,7 @@ rule_t *make_rule(void)
     r->uid = ++rule_uid;
     r->effect.floating = false;
     r->effect.follow = false;
-    r->effect.monitor = NULL;
-    r->effect.desktop = NULL;
+    r->effect.desc[0] = '\0';
     r->prev = NULL;
     r->next = NULL;
     return r;
diff --git a/types.h b/types.h
index 5ba942d21974d4b537d6f21c7ddf7eba297fa8fb..6e0ef8204699ba52ab83857669b7b56b3660eadc 100644 (file)
--- a/types.h
+++ b/types.h
@@ -37,78 +37,61 @@ typedef enum {
 } value_change_t;
 
 typedef enum {
-    LIST_OPTION_VERBOSE,
-    LIST_OPTION_QUIET
-} list_option_t;
+    CLIENT_TYPE_ALL,
+    CLIENT_TYPE_FLOATING,
+    CLIENT_TYPE_TILED
+} client_type_t;
 
 typedef enum {
-    SEND_OPTION_FOLLOW,
-    SEND_OPTION_DONT_FOLLOW
-} send_option_t;
+    CLIENT_CLASS_ALL,
+    CLIENT_CLASS_EQUAL,
+    CLIENT_CLASS_DIFFER
+} client_class_t;
 
-typedef enum {
-    SWAP_OPTION_KEEP_FOCUS,
-    SWAP_OPTION_SWAP_FOCUS
-} swap_option_t;
-
-typedef enum {
-    CANCEL_OPTION_FOCUSED,
-    CANCEL_OPTION_ALL
-} cancel_option_t;
+typedef struct {
+    client_type_t type;
+    client_class_t class;
+} client_select_t;
 
 typedef enum {
-    CLIENT_SKIP_NONE,
-    CLIENT_SKIP_FLOATING,
-    CLIENT_SKIP_TILED,
-    CLIENT_SKIP_CLASS_EQUAL,
-    CLIENT_SKIP_CLASS_DIFFER
-} skip_client_t;
+    ALTER_NONE,
+    ALTER_TOGGLE,
+    ALTER_SET
+} state_alter_t;
 
 typedef enum {
-    DESKTOP_SKIP_NONE,
-    DESKTOP_SKIP_FREE,
-    DESKTOP_SKIP_OCCUPIED
-} skip_desktop_t;
+    DESKTOP_ALL,
+    DESKTOP_FREE,
+    DESKTOP_OCCUPIED
+} desktop_select_t;
 
 typedef enum {
     CYCLE_NEXT,
     CYCLE_PREV
 } cycle_dir_t;
 
-typedef enum {
-    NEAREST_OLDER,
-    NEAREST_NEWER
-} nearest_arg_t;
-
 typedef enum {
     CIRCULATE_FORWARD,
     CIRCULATE_BACKWARD
 } circulate_dir_t;
 
-typedef enum {
-    ROTATE_IDENTITY,
-    ROTATE_CLOCKWISE,
-    ROTATE_COUNTER_CLOCKWISE,
-    ROTATE_FULL_CYCLE
-} rotate_t;
-
 typedef enum {
     FLIP_HORIZONTAL,
     FLIP_VERTICAL
 } flip_t;
 
 typedef enum {
-    DIR_LEFT,
     DIR_RIGHT,
-    DIR_UP,
-    DIR_DOWN
+    DIR_DOWN,
+    DIR_LEFT,
+    DIR_UP
 } direction_t;
 
 typedef enum {
     CORNER_TOP_LEFT,
     CORNER_TOP_RIGHT,
-    CORNER_BOTTOM_LEFT,
-    CORNER_BOTTOM_RIGHT
+    CORNER_BOTTOM_RIGHT,
+    CORNER_BOTTOM_LEFT
 } corner_t;
 
 typedef enum {
@@ -128,7 +111,6 @@ typedef enum {
 
 typedef struct {
     xcb_window_t window;
-    unsigned int uid;
     char class_name[MAXLEN];
     unsigned int border_width;
     bool floating;
@@ -146,7 +128,7 @@ struct node_t {
     double split_ratio;
     split_mode_t split_mode;
     direction_t split_dir;
-    rotate_t birth_rotation;
+    int birth_rotation;
     xcb_rectangle_t rectangle;
     bool vacant;          /* vacant nodes only hold floating clients */
     node_t *first_child;
@@ -197,6 +179,12 @@ struct monitor_t {
     monitor_t *next;
 };
 
+typedef struct {
+    monitor_t *monitor;
+    desktop_t *desktop;
+    node_t *node;
+} coordinates_t;
+
 typedef struct {
     char name[MAXLEN];
 } rule_cause_t;
@@ -204,8 +192,7 @@ typedef struct {
 typedef struct {
     bool floating;
     bool follow;
-    monitor_t *monitor;
-    desktop_t *desktop;
+    char desc[MAXLEN];
 } rule_effect_t;
 
 typedef struct rule_t rule_t;
@@ -217,17 +204,6 @@ struct rule_t {
     rule_t *next;
 };
 
-typedef struct {
-    node_t *node;
-    desktop_t *desktop;
-    monitor_t *monitor;
-} window_location_t;
-
-typedef struct {
-    desktop_t *desktop;
-    monitor_t *monitor;
-} desktop_location_t;
-
 typedef struct {
     xcb_point_t position;
     pointer_action_t action;
index 680c610cb91f5c2d610d6de0d85158d4ec985faf..0e0d68adf3427bad755222c31f12636359c22d08 100644 (file)
--- a/window.c
+++ b/window.c
@@ -11,6 +11,7 @@
 #include "settings.h"
 #include "ewmh.h"
 #include "rules.h"
+#include "query.h"
 #include "window.h"
 
 void center(xcb_rectangle_t a, xcb_rectangle_t *b)
@@ -35,32 +36,6 @@ bool might_cover(desktop_t *d, node_t *n)
     return false;
 }
 
-bool locate_window(xcb_window_t win, window_location_t *loc)
-{
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-                if (n->client->window == win) {
-                    loc->monitor = m;
-                    loc->desktop = d;
-                    loc->node = n;
-                    return true;
-                }
-    return false;
-}
-
-bool locate_desktop(char *name, desktop_location_t *loc)
-{
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            if (strcmp(d->name, name) == 0) {
-                loc->monitor = m;
-                loc->desktop = d;
-                return true;
-            }
-    return false;
-}
-
 bool is_inside(monitor_t *m, xcb_point_t pt)
 {
     xcb_rectangle_t r = m->rectangle;
@@ -115,7 +90,7 @@ monitor_t *underlying_monitor(client_t *c)
 
 void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
 {
-    window_location_t loc;
+    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;
 
@@ -157,13 +132,13 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win)
     disable_shadow(c->window);
 
     if (floating)
-        toggle_floating(d, n);
+        set_floating(d, n, true);
 
     if (d->focus != NULL && d->focus->client->fullscreen)
-        toggle_fullscreen(d, d->focus);
+        set_fullscreen(d, d->focus, false);
 
     if (fullscreen)
-        toggle_fullscreen(d, n);
+        set_fullscreen(d, n, true);
 
     if (is_tiled(c))
         window_lower(c->window);
@@ -215,7 +190,7 @@ void adopt_orphans(void)
         xcb_window_t win = wins[i];
         window_hide(win);
         if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
-            desktop_location_t loc;
+            coordinates_t loc;
             if (ewmh_locate_desktop(idx, &loc))
                 manage_window(loc.monitor, loc.desktop, win);
             else
@@ -328,7 +303,7 @@ void window_close(node_t *n)
     xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) &e);
 }
 
-void window_kill(monitor_t *m, desktop_t *d, node_t *n)
+void window_kill(desktop_t *d, node_t *n)
 {
     if (n == NULL)
         return;
@@ -338,44 +313,42 @@ void window_kill(monitor_t *m, desktop_t *d, node_t *n)
 
     xcb_kill_client(dpy, win);
     remove_node(d, n);
-    arrange(m, d);
 }
 
-void toggle_fullscreen(desktop_t *d, node_t *n)
+void set_fullscreen(desktop_t *d, node_t *n, bool value)
 {
-    if (n == NULL)
+    if (n == NULL || n->client->fullscreen == value)
         return;
 
     client_t *c = n->client;
 
-    PRINTF("toggle fullscreen %X\n", c->window);
+    PRINTF("fullscreen %X: %s\n", c->window, BOOLSTR(value));
 
-    if (c->fullscreen) {
-        c->fullscreen = false;
-        xcb_atom_t values[] = {XCB_NONE};
-        xcb_ewmh_set_wm_state(ewmh, c->window, LENGTH(values), values);
-        stack(d, n);
-    } else {
+    if (value) {
         c->fullscreen = true;
         xcb_atom_t values[] = {ewmh->_NET_WM_STATE_FULLSCREEN};
         xcb_ewmh_set_wm_state(ewmh, c->window, LENGTH(values), values);
         window_raise(c->window);
+    } else {
+        c->fullscreen = false;
+        xcb_atom_t values[] = {XCB_NONE};
+        xcb_ewmh_set_wm_state(ewmh, c->window, LENGTH(values), values);
+        stack(d, n);
     }
 }
 
-void toggle_floating(desktop_t *d, node_t *n)
+void set_floating(desktop_t *d, node_t *n, bool value)
 {
-    if (n == NULL || n->client->transient || n->client->fullscreen)
+    if (n == NULL || n->client->transient || n->client->fullscreen || n->client->floating == value)
         return;
 
-    PRINTF("toggle floating %X\n", n->client->window);
+    PRINTF("floating %X: %s\n", n->client->window, BOOLSTR(value));
 
     n->split_mode = MODE_AUTOMATIC;
     client_t *c = n->client;
-    c->floating = !c->floating;
-    n->vacant = !n->vacant;
+    c->floating = n->vacant = value;
     update_vacant_state(n->parent);
-    if (c->floating) {
+    if (value) {
         enable_shadow(c->window);
         unrotate_brother(n);
     } else {
@@ -385,16 +358,16 @@ void toggle_floating(desktop_t *d, node_t *n)
     stack(d, n);
 }
 
-void toggle_locked(monitor_t *m, desktop_t *d, node_t *n)
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
 {
-    if (n == NULL)
+    if (n == NULL || n->client->locked == value)
         return;
 
     client_t *c = n->client;
 
-    PRINTF("toggle locked %X\n", c->window);
+    PRINTF("set locked %X: %s\n", c->window, BOOLSTR(value));
 
-    c->locked = !c->locked;
+    c->locked = value;
     window_draw_border(n, d->focus == n, m == mon);
 }
 
@@ -424,18 +397,6 @@ void disable_shadow(xcb_window_t win)
     set_shadow(win, 0);
 }
 
-void list_windows(char *rsp)
-{
-    char line[MAXLEN];
-
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
-            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                snprintf(line, sizeof(line), "0x%X\n", n->client->window);
-                strncat(rsp, line, REMLEN(rsp));
-            }
-}
-
 uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor)
 {
     if (c == NULL)
@@ -492,7 +453,7 @@ void query_pointer(xcb_window_t *win, xcb_point_t *pt)
 
 void window_focus(xcb_window_t win)
 {
-    window_location_t loc;
+    coordinates_t loc;
     if (locate_window(win, &loc)) {
         if (loc.node == mon->desk->focus)
             return;
@@ -577,9 +538,9 @@ void window_show(xcb_window_t win)
 
 void toggle_visibility(void)
 {
-    if (visible)
-        clear_input_focus();
     visible = !visible;
+    if (!visible)
+        clear_input_focus();
     for (monitor_t *m = mon_head; m != NULL; m = m->next)
         for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
             window_set_visibility(n->client->window, visible);
index 3a1a097be31d75f8123e666898f6bbcdc54cc320..23945905d5768d06dd1fce96ccc1ed79f92cd6e6 100644 (file)
--- a/window.h
+++ b/window.h
@@ -9,8 +9,6 @@
 void center(xcb_rectangle_t, xcb_rectangle_t *);
 bool contains(xcb_rectangle_t, xcb_rectangle_t);
 bool might_cover(desktop_t *, node_t *);
-bool locate_window(xcb_window_t, window_location_t *);
-bool locate_desktop(char *, desktop_location_t *);
 bool is_inside(monitor_t *, xcb_point_t);
 xcb_rectangle_t get_rectangle(client_t *);
 void get_side_handle(client_t *, direction_t, xcb_point_t *);
@@ -22,12 +20,11 @@ void window_draw_border(node_t *, bool, bool);
 uint32_t get_border_color(client_t *, bool, bool);
 void update_floating_rectangle(client_t *);
 void query_pointer(xcb_window_t *, xcb_point_t *);
-void list_windows(char *);
 void window_close(node_t *);
-void window_kill(monitor_t *, desktop_t *, node_t *);
-void toggle_fullscreen(desktop_t *, node_t *);
-void toggle_floating(desktop_t *, node_t *);
-void toggle_locked(monitor_t *, desktop_t *, node_t *);
+void window_kill(desktop_t *, node_t *);
+void set_fullscreen(desktop_t *, node_t *, bool);
+void set_floating(desktop_t *, node_t *, bool);
+void set_locked(monitor_t *, desktop_t *, node_t *, bool);
 void set_urgency(monitor_t *, desktop_t *, node_t *, bool);
 void set_shadow(xcb_window_t, uint32_t);
 void enable_shadow(xcb_window_t);