[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig: https://editorconfig.org\n\n# Top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*]\ninsert_final_newline = true\ntrim_trailing_whitespace = true\ncharset = utf-8\nindent_style = tab\nindent_size = 4\n"
  },
  {
    "path": ".gitignore",
    "content": "tags\nbspwm\nbspc\n*.o\ntests/test_window\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012, Bastien Dejean\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "VERCMD  ?= git describe --tags 2> /dev/null\nVERSION := $(shell $(VERCMD) || cat VERSION)\n\nCPPFLAGS += -D_POSIX_C_SOURCE=200809L -DVERSION=\\\"$(VERSION)\\\"\nCFLAGS   += -std=c99 -pedantic -Wall -Wextra -DJSMN_STRICT\nLDFLAGS  ?=\nLDLIBS    = $(LDFLAGS) -lm -lxcb -lxcb-util -lxcb-keysyms -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama -lxcb-shape\n\nPREFIX    ?= /usr/local\nBINPREFIX ?= $(PREFIX)/bin\nMANPREFIX ?= $(PREFIX)/share/man\nDOCPREFIX ?= $(PREFIX)/share/doc/bspwm\nBASHCPL   ?= $(PREFIX)/share/bash-completion/completions\nFISHCPL   ?= $(PREFIX)/share/fish/vendor_completions.d\nZSHCPL    ?= $(PREFIX)/share/zsh/site-functions\n\nMD_DOCS    = README.md doc/CHANGELOG.md doc/CONTRIBUTING.md doc/INSTALL.md doc/MISC.md doc/TODO.md\nXSESSIONS ?= $(PREFIX)/share/xsessions\n\nWM_SRC   = bspwm.c helpers.c geometry.c jsmn.c settings.c monitor.c desktop.c tree.c stack.c history.c \\\n\t events.c pointer.c window.c messages.c parse.c query.c restore.c rule.c ewmh.c subscribe.c\nWM_OBJ  := $(WM_SRC:.c=.o)\nCLI_SRC  = bspc.c helpers.c\nCLI_OBJ := $(CLI_SRC:.c=.o)\n\nall: bspwm bspc\n\ndebug: CFLAGS += -O0 -g\ndebug: bspwm bspc\n\nVPATH=src\n\ninclude Sourcedeps\n\n$(WM_OBJ) $(CLI_OBJ): Makefile\n\nbspwm: $(WM_OBJ)\n\nbspc: $(CLI_OBJ)\n\ninstall:\n\tmkdir -p \"$(DESTDIR)$(BINPREFIX)\"\n\tcp -pf bspwm \"$(DESTDIR)$(BINPREFIX)\"\n\tcp -pf bspc \"$(DESTDIR)$(BINPREFIX)\"\n\tmkdir -p \"$(DESTDIR)$(MANPREFIX)\"/man1\n\tcp -p doc/bspwm.1 \"$(DESTDIR)$(MANPREFIX)\"/man1\n\tcp -Pp doc/bspc.1 \"$(DESTDIR)$(MANPREFIX)\"/man1\n\tmkdir -p \"$(DESTDIR)$(BASHCPL)\"\n\tcp -p contrib/bash_completion \"$(DESTDIR)$(BASHCPL)\"/bspc\n\tmkdir -p \"$(DESTDIR)$(FISHCPL)\"\n\tcp -p contrib/fish_completion \"$(DESTDIR)$(FISHCPL)\"/bspc.fish\n\tmkdir -p \"$(DESTDIR)$(ZSHCPL)\"\n\tcp -p contrib/zsh_completion \"$(DESTDIR)$(ZSHCPL)\"/_bspc\n\tmkdir -p \"$(DESTDIR)$(DOCPREFIX)\"\n\tcp -p $(MD_DOCS) \"$(DESTDIR)$(DOCPREFIX)\"\n\tmkdir -p \"$(DESTDIR)$(DOCPREFIX)\"/examples\n\tcp -pr examples/* \"$(DESTDIR)$(DOCPREFIX)\"/examples\n\tmkdir -p \"$(DESTDIR)$(XSESSIONS)\"\n\tcp -p contrib/freedesktop/bspwm.desktop \"$(DESTDIR)$(XSESSIONS)\"\n\nuninstall:\n\trm -f \"$(DESTDIR)$(BINPREFIX)\"/bspwm\n\trm -f \"$(DESTDIR)$(BINPREFIX)\"/bspc\n\trm -f \"$(DESTDIR)$(MANPREFIX)\"/man1/bspwm.1\n\trm -f \"$(DESTDIR)$(MANPREFIX)\"/man1/bspc.1\n\trm -f \"$(DESTDIR)$(BASHCPL)\"/bspc\n\trm -f \"$(DESTDIR)$(FISHCPL)\"/bspc.fish\n\trm -f \"$(DESTDIR)$(ZSHCPL)\"/_bspc\n\trm -rf \"$(DESTDIR)$(DOCPREFIX)\"\n\trm -f \"$(DESTDIR)$(XSESSIONS)\"/bspwm.desktop\n\ndoc:\n\ta2x -v -d manpage -f manpage -a revnumber=$(VERSION) doc/bspwm.1.asciidoc\n\nclean:\n\trm -f $(WM_OBJ) $(CLI_OBJ) bspwm bspc\n\n.PHONY: all debug install uninstall doc clean\n"
  },
  {
    "path": "README.md",
    "content": "## Description\n\n*bspwm* is a tiling window manager that represents windows as the leaves of a full binary tree.\n\nIt only responds to X events, and the messages it receives on a dedicated socket.\n\n*bspc* is a program that writes messages on *bspwm*'s socket.\n\n*bspwm* doesn't handle any keyboard or pointer inputs: a third party program (e.g. *sxhkd*) is needed in order to translate keyboard and pointer events to *bspc* invocations.\n\nThe outlined architecture is the following:\n\n```\n        PROCESS          SOCKET\nsxhkd  -------->  bspc  <------>  bspwm\n```\n\n## Configuration\n\nThe default configuration file is `$XDG_CONFIG_HOME/bspwm/bspwmrc`: this is simply a shell script that calls *bspc*.\n\nAn argument is passed to that script to indicate whether is was executed after a restart (`$1 -gt 0`) or not (`$1 -eq 0`).\n\nKeyboard and pointer bindings are defined with [sxhkd](https://github.com/baskerville/sxhkd).\n\nExample configuration files can be found in the [examples](examples) directory.\n\n## Monitors, desktops and windows\n\n*bspwm* holds a list of monitors.\n\nA monitor is just a rectangle that contains desktops.\n\nA desktop is just a pointer to a tree.\n\nMonitors only show the tree of one desktop at a time (their focused desktop).\n\nThe tree is a partition of a monitor's rectangle into smaller rectangular regions.\n\nEach node in a tree either has zero or two children.\n\nEach internal node is responsible for splitting a rectangle in half.\n\nA split is defined by two parameters: the type (horizontal or vertical) and the ratio (a real number *r* such that *0 < r < 1*).\n\nEach leaf node holds exactly one window.\n\n## Insertion modes\n\nWhen *bspwm* receives a new window, it inserts it into a window tree at the specified insertion point (a leaf) using the insertion mode specified for that insertion point.\n\nThe insertion mode tells *bspwm* how it should alter the tree in order to insert new windows on a given insertion point.\n\nBy default the insertion point is the focused window and its insertion mode is *automatic*.\n\n### Manual mode\n\nThe user can specify a region in the insertion point where the next new window should appear by sending a *node -p|--presel-dir DIR* message to *bspwm*.\n\nThe *DIR* argument allows to specify how the insertion point should be split (horizontally or vertically) and if the new window should be the first or the second child of the new internal node (the insertion point will become its *brother*).\n\nAfter doing so the insertion point goes into *manual* mode.\n\nLet's consider the following scenario:\n\n```\n            a                          a                          a\n           / \\                        / \\                        / \\\n          1   b         --->         c   b         --->         c   b\n          ^  / \\                    / \\ / \\                    / \\ / \\\n            2   3                  4  1 2  3                  d  1 2  3\n                                   ^                         / \\\n                                                            5   4\n                                                            ^\n\n+-----------------------+  +-----------------------+  +-----------------------+\n|           |           |  |           |           |  |     |     |           |\n|           |     2     |  |     4     |     2     |  |  5  |  4  |     2     |\n|           |           |  |     ^     |           |  |  ^  |     |           |\n|     1     |-----------|  |-----------|-----------|  |-----------|-----------|\n|     ^     |           |  |           |           |  |           |           |\n|           |     3     |  |     1     |     3     |  |     1     |     3     |\n|           |           |  |           |           |  |           |           |\n+-----------------------+  +-----------------------+  +-----------------------+\n\n            X                          Y                          Z \n```\n\nIn state *X*, the insertion point is *1*.\n\nWe send the following message to *bspwm*: *node -p north*.\n\nThen add a new window: *4*, this leads to state *Y*: the new internal node, *c* becomes *a*'s first child.\n\nFinally we send another message: *node -p west* and add window *5*.\n\nThe ratio of the preselection (that ends up being the ratio of the split of the new internal node) can be changed with the *node -o|--presel-ratio* message.\n\n### Automatic mode\n\nThe *automatic* mode, as opposed to the *manual* mode, doesn't require any user choice. The way the new window is inserted is determined by the value of the automatic scheme and the initial polarity settings.\n\n#### Longest side scheme\n\nWhen the value of the automatic scheme is `longest_side`, the window will be attached as if the insertion point was in manual mode and the split direction was chosen based on the dimensions of the tiling rectangle and the initial polarity.\n\nLet's consider the following scenario, where the initial polarity is set to `second_child`:\n\n```\n             1                          a                          a\n             ^                         / \\                        / \\\n                         --->         1   2         --->         1   b\n                                          ^                         / \\\n                                                                   2   3\n                                                                       ^\n\n +-----------------------+  +-----------------------+  +-----------------------+\n |                       |  |           |           |  |           |           |\n |                       |  |           |           |  |           |     2     |\n |                       |  |           |           |  |           |           |\n |           1           |  |     1     |     2     |  |     1     |-----------|\n |           ^           |  |           |     ^     |  |           |           |\n |                       |  |           |           |  |           |     3     |\n |                       |  |           |           |  |           |     ^     |\n +-----------------------+  +-----------------------+  +-----------------------+\n\n             X                          Y                          Z\n```\n\nIn state *X*, a new window is added.\n\nSince *1* is wide, it gets split vertically and *2* is added as *a*'s second child given the initial polarity.\n\nThis leads to *Y* where we insert window *3*. *2* is tall and is therefore split horizontally. *3* is once again added as *b*'s second child.\n\n#### Alternate scheme\n\nWhen the value of the automatic scheme is `alternate`, the window will be attached as if the insertion point was in manual mode and the split direction was chosen based on the split type of the insertion point's parent and the initial polarity. If the parent is split horizontally, the insertion point will be split vertically and vice versa.\n\n#### Spiral scheme\n\nWhen the value of the automatic scheme is `spiral`, the window will *take the space* of the insertion point.\n\nLet's dive into the details with the following scenario:\n\n```\n             a                          a                          a\n            / \\                        / \\                        / \\\n           1   b         --->         1   c         --->         1   d\n              / \\                        / \\                        / \\\n             2   3                      4   b                      5   c\n             ^                          ^  / \\                     ^  / \\\n                                          3   2                      b   4\n                                                                    / \\\n                                                                   3   2\n\n +-----------------------+  +-----------------------+  +-----------------------+\n |           |           |  |           |           |  |           |           |\n |           |     2     |  |           |     4     |  |           |     5     |\n |           |     ^     |  |           |     ^     |  |           |     ^     |\n |     1     |-----------|  |     1     |-----------|  |     1     |-----------|\n |           |           |  |           |     |     |  |           |  3  |     |\n |           |     3     |  |           |  3  |  2  |  |           |-----|  4  |\n |           |           |  |           |     |     |  |           |  2  |     |\n +-----------------------+  +-----------------------+  +-----------------------+\n\n             X                          Y                          Z\n```\n\nIn state *X*, the insertion point, *2* is in automatic mode.\n\nWhen we add a new window, *4*, the whole tree rooted at *b* is reattached, as the second child of a new internal node, *c*.\n\nThe splitting parameters of *b* (type: *horizontal*, ratio: *½*) are copied to *c* and *b* is rotated by 90° clockwise.\n\nThe tiling rectangle of *4* in state *Y* is equal to the tiling rectangle of *2* in state *X*.\n\nThen the insertion of *5*, with *4* as insertion point, leads to *Z*.\n\nThe *spiral* automatic scheme generates window spirals that rotate clockwise (resp. anti-clockwise) if the insertion point is the first (resp. second) child of its parent.\n\n\n## Supported protocols and standards\n\n- The RandR and Xinerama protocols.\n- A subset of the EWMH and ICCCM standards.\n\n## Community\n\nWant to get in touch with other *bspwm* users or you need help? Join us on our:\n\n- Subreddit at [r/bspwm](https://www.reddit.com/r/bspwm/).\n- IRC channel at `#bspwm` on `irc.libera.chat` (maintained by [Emanuele Torre](https://github.com/emanuele6) / emanuele6 on IRC).\n- Matrix room at https://matrix.to/#/#bspwm:matrix.org\n"
  },
  {
    "path": "Sourcedeps",
    "content": "bspc.o: bspc.c common.h helpers.h\nbspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h pointer.h rule.h settings.h subscribe.h types.h window.h\ndesktop.o: desktop.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h\nevents.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h\newmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h\ngeometry.o: geometry.c geometry.h helpers.h types.h\nhelpers.o: helpers.c bspwm.h helpers.h types.h\nhistory.o: history.c bspwm.h helpers.h query.h tree.h types.h\njsmn.o: jsmn.c jsmn.h\nmessages.o: messages.c bspwm.h common.h desktop.h helpers.h jsmn.h messages.h monitor.h parse.h pointer.h query.h restore.h rule.h settings.h subscribe.h tree.h types.h window.h\nmonitor.o: monitor.c bspwm.h desktop.h ewmh.h geometry.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h\nparse.o: parse.c helpers.h parse.h subscribe.h types.h\npointer.o: pointer.c bspwm.h events.h helpers.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h\nquery.o: query.c bspwm.h desktop.h helpers.h history.h monitor.h parse.h query.h subscribe.h tree.h types.h window.h\nrestore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h pointer.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h window.h\nrule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h subscribe.h types.h window.h\nsettings.o: settings.c bspwm.h helpers.h settings.h types.h\nstack.o: stack.c bspwm.h ewmh.h helpers.h stack.h subscribe.h tree.h types.h window.h\nsubscribe.o: subscribe.c bspwm.h desktop.h helpers.h settings.h subscribe.h types.h\ntree.o: tree.c bspwm.h desktop.h ewmh.h geometry.h helpers.h history.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h\nwindow.o: window.c bspwm.h ewmh.h geometry.h helpers.h monitor.h parse.h pointer.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h\n"
  },
  {
    "path": "VERSION",
    "content": "0.9.12"
  },
  {
    "path": "contrib/bash_completion",
    "content": "_bspc() {\n\tlocal commands='node desktop monitor query rule wm subscribe config quit'\n\n\tlocal settings='external_rules_command status_prefix normal_border_color active_border_color focused_border_color presel_feedback_color border_width window_gap top_padding right_padding bottom_padding left_padding top_monocle_padding right_monocle_padding bottom_monocle_padding left_monocle_padding split_ratio automatic_scheme removal_adjustment initial_polarity directional_focus_tightness presel_feedback borderless_monocle gapless_monocle single_monocle borderless_singleton pointer_motion_interval pointer_modifier pointer_action1 pointer_action2 pointer_action3 click_to_focus swallow_first_click focus_follows_pointer pointer_follows_focus pointer_follows_monitor mapping_events_count ignore_ewmh_focus ignore_ewmh_fullscreen ignore_ewmh_struts center_pseudo_tiled honor_size_hints remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'\n\n\tCOMPREPLY=()\n\n\tif [[ $COMP_CWORD -ge 1 ]] ; then\n\t\tlocal current_word=\"${COMP_WORDS[COMP_CWORD]}\"\n\t\tif [[ $COMP_CWORD -eq 1 ]] ; then\n\t\t\tCOMPREPLY=( $(compgen -W \"$commands\" -- \"$current_word\") )\n\t\t\treturn 0\n\t\telse\n\t\t\tlocal second_word=${COMP_WORDS[1]}\n\t\t\tcase $second_word in\n\t\t\t\tconfig)\n\t\t\t\t\tif [[ $COMP_CWORD -eq 2 ]] ; then\n\t\t\t\t\t\tCOMPREPLY=( $(compgen -W \"$settings\" -- \"$current_word\") )\n\t\t\t\t\t\treturn 0\n\t\t\t\t\tfi\n\t\t\t\t\t;;\n\t\t\tesac\n\t\tfi\n\tfi\n}\n\ncomplete -F _bspc bspc\n\n# vim: set ft=sh:\n"
  },
  {
    "path": "contrib/fish_completion",
    "content": "function __fish_bspc_needs_command\n  set cmd (commandline -opc)\n  [ (count $cmd) -eq 1 -a $cmd[1] = 'bspc' ]; and return 0\n  return 1\nend\n\nfunction __fish_bspc_using_command\n  set cmd (commandline -opc)\n  [ (count $cmd) -gt 1 ]; and [ $argv[1] = $cmd[2] ]; and return 0\n  return 1\nend\n\ncomplete -f -c bspc -n '__fish_bspc_needs_command' -a 'node desktop monitor query rule wm subscribe config quit'\ncomplete -f -c bspc -n '__fish_bspc_using_command config' -a 'external_rules_command status_prefix normal_border_color active_border_color focused_border_color presel_feedback_color border_width window_gap top_padding right_padding bottom_padding left_padding top_monocle_padding right_monocle_padding bottom_monocle_padding left_monocle_padding split_ratio automatic_scheme removal_adjustment initial_polarity directional_focus_tightness presel_feedback borderless_monocle gapless_monocle single_monocle borderless_singleton pointer_motion_interval pointer_modifier pointer_action1 pointer_action2 pointer_action3 click_to_focus swallow_first_click focus_follows_pointer pointer_follows_focus pointer_follows_monitor mapping_events_count ignore_ewmh_focus ignore_ewmh_fullscreen ignore_ewmh_struts center_pseudo_tiled honor_size_hints remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'\n"
  },
  {
    "path": "contrib/freedesktop/bspwm.desktop",
    "content": "[Desktop Entry]\nName=bspwm\nComment=Binary space partitioning window manager\nExec=bspwm\nType=Application\nDesktopNames=bspwm\n"
  },
  {
    "path": "contrib/zsh_completion",
    "content": "#compdef bspc\n\n_bspc_selector() {\n\t[[ ${@[(r)--]} = '--' ]] && shift ${@[(i)--]}\n\tlocal -a completions=() completions_display=()\n\tlocal index=0 name id sel_type=\"$1\"\n\tshift 1\n\tcase $sel_type in\n\t\t(node) compset -P '*[#.:/@]' ;;\n\t\t(desktop) compset -P '*[#.:]' ;;\n\t\t(monitor) compset -P '*[#.]' ;;\n\t\t(*) return 1 ;;\n\tesac\n\tcase \"$sel_type $IPREFIX\" in\n\t\t(desktop*:)\n\t\t\tlocal ipfx=${${IPREFIX##*@}%:}\n\t\t\twhile do\n\t\t\t\tif completions=(\"${(@f)$(bspc query --names -D -m ${ipfx} 2> /dev/null)}\") ;then\n\t\t\t\t\tuntil ((++index > $#completions)) do\n\t\t\t\t\t\tcompletions[index]=\"^${index}:${completions[index]}\"\n\t\t\t\t\tdone\n\t\t\t\t\tcompletions+='focused'\n\t\t\t\t\t_describe \"${sel_type} selector\" completions $@ -S '' -J ${sel_type}\n\t\t\t\t\tbreak\n\t\t\t\telse\n\t\t\t\t\tcompletions=()\n\t\t\t\t\t[ -n \"$ipfx[(r)#]\" ] &&\n\t\t\t\t\t\tipfx=\"${ipfx#*#}\" ||\n\t\t\t\t\t\tbreak\n\t\t\t\tfi\n\t\t\tdone\n\t\t\t;|\n\t\t(node*[^@/:.])\n\t\t\tbspc query -N -n .window 2> /dev/null |\n\t\t\t\twhile read id ;do\n\t\t\t\t\tid=${id//:/\\\\:}\n\t\t\t\t\tif which xdotool &> /dev/null ;then\n\t\t\t\t\t\tname=$(xdotool getwindowname $id 2> /dev/null)\n\t\t\t\t\telif which xprop &> /dev/null ;then\n\t\t\t\t\t\tname=$(xprop -id $id -notype WM_NAME 2> /dev/null) &&\n\t\t\t\t\t\t\t[[ \"$name\" = 'WM_NAME ='* ]] &&\n\t\t\t\t\t\t\tname=\"${${name#*\\\"}%\\\"*}\" ||\n\t\t\t\t\t\t\tname=\"\"\n\t\t\t\t\telse\n\t\t\t\t\t\tname=\"install xdotool or xprop to see window titles here\"\n\t\t\t\t\tfi\n\t\t\t\t\tcompletions+=\"$id:$name\"\n\t\t\t\tdone\n\t\t\t;|\n\t\t((desktop|monitor)*[^:.])\n\t\t\tlocal max_name_len=0 max_index_len i\n\t\t\tlocal -a snames names ids\n\t\t\tbspc query -${(U)sel_type[1]} 2> /dev/null |\n\t\t\t\twhile read id ;do\n\t\t\t\t\t((index++))\n\t\t\t\t\tname=$(bspc query --names -${(U)sel_type[1]} -${sel_type[1]} $id 2> /dev/null)\n\t\t\t\t\t[[ \"$name\" == *[:.!]* ]] &&\n\t\t\t\t\t\tsel_name=\"%${name//:/\\\\:}\" ||\n\t\t\t\t\t\tsel_name=\"$name\"\n\t\t\t\t\t((max_name_len = $#sel_name > max_name_len ? $#sel_name : max_name_len))\n\t\t\t\t\tids+=\"${id}\"\n\t\t\t\t\tsnames+=\"${sel_name}\"\n\t\t\t\t\tnames+=\"${name}\"\n\t\t\t\tdone\n\t\t\t\tmax_index_len=$(($#index + 1))\n\t\t\t\t((max_name_len >= max_name_len)) && ((max_name_len=max_name_len + 1))\n\t\t\t\tfor ((i = 1 ; i <= $#ids ; i++)) ;do\n\t\t\t\t\t(($#ids[i] <= max_name_len)) &&\n\t\t\t\t\t\tcompletions_display+=\"${(r($max_name_len+1))ids[i]}:${names[i]}\" ||\n\t\t\t\t\t\tcompletions_display+=\"${ids[i]}:${names[i]}\"\n\t\t\t\t\tcompletions+=\"${ids[i]}:${names[i]}\"\n\t\t\t\tdone\n\t\t\t\tfor ((i = 1 ; i <= $#ids ; i++)) ;do\n\t\t\t\t\tcompletions+=\"${snames[i]}:${names[i]}\"\n\t\t\t\t\tcompletions_display+=\"${(r($max_name_len))snames[i]}:${names[i]}\"\n\t\t\t\tdone\n\t\t\t\tfor ((i = 1 ; i <= $#ids ; i++)) ;do\n\t\t\t\t\tcompletions+=\"^${i}:${names[i]}\"\n\t\t\t\t\tcompletions_display+=\"^${i}:${names[i]}\"\n\t\t\t\tdone\n\t\t\t;|\n\t\t(node*('@'(*':'|*'/'|)))\n\t\t\t_describe 'node path' jump -S '/' -r \"#. ${quote}\" -J nodes\n\t\t\t;|\n\t\t(node*'/')\n\t\t\t;;\n\t\t(*'.')\n\t\t\t_bspc_prefix '!' \"${sel_type} modifiers\" ${sel_type}_mod $@ -J ${sel_type}_mod\n\t\t\t;|\n\t\t((desktop|monitor)*@)\n\t\t\t;&\n\t\t(*[^:.@/])\n\t\t\tif (( $#completions_display)) ;then\n\t\t\t_describe \"${sel_type} selector\" ${sel_type}_desc $@ -S '.' -r \". \\n:#${quote}\\-\" -J ${sel_type} \\\n\t\t\t\t-- completions_display completions $@ -S '.' -r \". \\n:#${quote}\\-\" -J ${sel_type}\n\t\t\telse\n\t\t\t_describe \"${sel_type} selector\" ${sel_type}_desc $@ -S '.' -r \". \\n:#${quote}\\-\" -J ${sel_type} \\\n\t\t\t\t-- completions $@ -S '.' -r \". \\n:#${quote}\\-\" -J ${sel_type}\n\t\t\tfi\n\t\t\t;|\n\t\t(node*@*'#'*)\n\t\t\t;;\n\t\t(node*@*)\n\t\t\t_bspc_selector desktop -S ':' -qr \".#\\-\\n ${quote}\"\n\t\t\t;;\n\t\t(desktop*)\n\t\t\t_bspc_selector monitor -S ':' -r \".#\\-\\n ${quote}\"\n\t\t\t;;\n\tesac\n}\n\n_bspc_prefix(){\n\t[[ ${@[(r)--]} = '--' ]] && shift ${@[(i)--]}\n\t[[ \"$PREFIX[1]\" == \"$1\" ]] &&\n\t\tlocal a=\"-n\" b ||\n\t\tlocal b=\"-n\" a\n\t_describe $@[2,-1] $a -- $@[3,-1] $b -p \"$1\"\n}\n\n_bspc_query_names() {\n\t[[ ${@[(r)--]} = '--' ]] && shift ${@[(i)--]}\n\tlocal -a items=(\"${(@f)$(bspc query $2 --names 2> /dev/null)}\") ||\n\t\treturn\n\tlocal c\n\tfor c in '\\' ':' '[' '(' '*'\n\t\titems=(\"${(@)items//$c/\\\\$c}\")\n\t_values -w  \"$1\" \"${items[@]}\"\n}\n\n_bspc() {\n\tlocal -a commands=(node desktop monitor query rule wm subscribe config quit) \\\n\t\tresize_handle=(top bottom top_left top_right bottom_left bottom_right left right) \\\n\t\tnode_state=(tiled pseudo_tiled floating fullscreen) \\\n\t\tflag=(hidden sticky private locked marked urgent) \\\n\t\tlayer=(below normal above) \\\n\t\tdir=(north west south east) \\\n\t\tcycle_dir=(next prev)\n\tlocal -a jump=($dir first second brother parent 1 2) \\\n\t\tnode_desc=($dir $cycle_dir any last newest older newer focused pointed biggest smallest) \\\n\t\tnode_mod=($node_state $flag $layer focused automatic local \\\n\t\tactive leaf window same_class descendant_of ancestor_of) \\\n\t\tdesktop_desc=($cycle_dir any last newest older newer focused) \\\n\t\tdesktop_mod=(focused occupied local urgent) \\\n\t\tmonitor_desc=($dir $cycle_dir any last newest older newer focused pointed primary) \\\n\t\tmonitor_mod=(focused occupied) \\\n\t\tpresel_dir=($dir cancel)\n\tlocal quote=\"${compstate[quote]}\" context state state_descr line\n\ttypeset -A opt_args\n\n\tcompset -n 2\n\tcompset -S \"${quote}\"\n\n\tif ((CURRENT==1)) ;then\n\t\t_describe 'command or domain' commands\n\t\treturn\n\tfi\n\n\tcase $words[1] in\n\t\t(node)\n\t\t\t((CURRENT==2)) && _bspc_selector node\n\t\t\t((CURRENT>2)) && [[ \"$words[2]\" != '-'* ]] && compset -n 2\n\t\t\t((CURRENT>2)) && [[ \"$words[CURRENT-2]\" =~ \"^-(m|d|n|s|-to-(monitor|desktop|node)|-swap)$\" ]] &&\n\t\t\t\t_values 'option' '--follow[If passed, the focused node will stay focused]'\n\t\t\t_arguments -C \\\n\t\t\t\t'*'{-a,--activate}'[Activate the selected or given node]::node selector:_bspc_selector -- node'\\\n\t\t\t\t'*'{-B,--balance}'[Adjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area]'\\\n\t\t\t\t'*'{-C,--circulate}'[Circulate the windows of the tree rooted at the selected node]:direction:(forward backward)'\\\n\t\t\t\t'*'{-c,--close}'[Close the windows rooted at the selected node]'\\\n\t\t\t\t'*'{-d,--to-desktop}'[Send the selected node to the given desktop]:desktop selector:_bspc_selector -- desktop'\\\n\t\t\t\t'*'{-E,--equalize}'[Reset the split ratios of the tree rooted at the selected node to their default value]'\\\n\t\t\t\t'*'{-F,--flip}'[Flip the the tree rooted at selected node]: :(horizontal vertical)'\\\n\t\t\t\t'*'{-f,--focus}'[Focus the selected or given node]::node selector:_bspc_selector -- node'\\\n\t\t\t\t'*'{-g,--flag}'[Set or toggle the given flag for the selected node]: :-> flag'\\\n\t\t\t\t'*'{-i,--insert-receptacle}'[Insert a receptacle node at the selected node]'\\\n\t\t\t\t'*'{-k,--kill}'[Kill the windows rooted at the selected node]'\\\n\t\t\t\t'*'{-l,--layer}\"[Set the stacking layer of the selected window]:stacking layer:($layer)\"\\\n\t\t\t\t'*'{-m,--to-monitor}'[Send the selected node to the given monitor]:monitor selector:_bspc_selector -- monitor'\\\n\t\t\t\t'*'{-n,--to-node}'[Transplant the selected node to the given node]:node selector:_bspc_selector -- node'\\\n\t\t\t\t'*'{-o,--presel-ratio}'[Set the splitting ratio of the preselection area]:preselect ratio: ( )'\\\n\t\t\t\t'*'{-p,--presel-dir}'[Preselect the splitting area of the selected node or cancel the preselection]: :_bspc_prefix -- \"~\" preselect presel_dir'\\\n\t\t\t\t'*'{-y,--type}'[Set the splitting type of the selected node]: :(horizontal vertical)'\\\n\t\t\t\t'*'{-r,--ratio}'[Set the splitting ratio of the selected node (0 < ratio < 1)]: :( )'\\\n\t\t\t\t'*'{-R,--rotate}'[Rotate the tree rooted at the selected node]:angle:(90 270 180)'\\\n\t\t\t\t'*'{-s,--swap}'[Swap the selected node with the given node]:node selector:_bspc_selector -- node'\\\n\t\t\t\t'*'{-t,--state}'[Set the state of the selected window]: :_bspc_prefix -- \"~\" \"node state\" node_state '\\\n\t\t\t\t'*'{-v,--move}'[Move the selected window by dx pixels horizontally and dy pixels vertically]:dx:( ):dy:( )'\\\n\t\t\t\t'*'{-z,--resize}\"[Resize the selected window by moving the given handle by dx pixels horizontally and dy pixels vertically]:handle:($resize_handle):dx:( ):dy:( )\"\n\t\t\t[ \"$state\" = flag ] && _values 'flag' \"${flag[@]:#urgent}::set flag:(on off)\"\n\t\t\t;;\n\t\t(desktop)\n\t\t\t((CURRENT==2)) && _bspc_selector desktop\n\t\t\t((CURRENT>2)) && [[ \"$words[2]\" != '-'* ]] && compset -n 2\n\t\t\t((CURRENT>2)) && [[ \"$words[CURRENT-2]\" =~ \"^-m|-s|--monitor|--swap$\" ]] &&\n\t\t\t\t_values 'option' '--follow[If passed, the focused desktop will stay focused]'\n\t\t\t_arguments \\\n\t\t\t\t'*'{-a,--activate}'[Activate the selected or given desktop]:: :_bspc_selector -- desktop'\\\n\t\t\t\t'*'{-b,--bubble}\"[Bubble the selected desktop in the given direction]:direction:($cycle_dir)\"\\\n\t\t\t\t'*'{-f,--focus}'[Focus the selected or given desktop]:: :_bspc_selector -- desktop'\\\n\t\t\t\t'*'{-l,--layout}\"[Set or cycle the layout of the selected desktop]:desktop layout:($cycle_dir monocle tiled)\"\\\n\t\t\t\t'*'{-m,--to-monitor}'[Send the selected desktop to the given monitor]: :_bspc_selector -- monitor'\\\n\t\t\t\t'*'{-n,--rename}'[Rename the selected desktop]:desktop name:( )'\\\n\t\t\t\t'*'{-r,--remove}'[Remove the selected desktop]'\\\n\t\t\t\t'*'{-s,--swap}'[Swap the selected desktop with the given desktop]: :_bspc_selector -- desktop'\n\t\t\t;;\n\t\t(monitor)\n\t\t\t((CURRENT==2)) && _bspc_selector monitor\n\t\t\t((CURRENT>2)) && [[ \"$words[2]\" != '-'* ]] && compset -n 2\n\t\t\t_arguments \\\n\t\t\t\t'*'{-a,--add-desktops}'[Create desktops with the given names in the selected monitor]:*:add desktops:( )'\\\n\t\t\t\t'*'{-d,--reset-desktops}'[Rename, add or remove desktops]:*: :->desktops'\\\n\t\t\t\t'*'{-f,--focus}'[Focus the selected or given monitor]:: :_bspc_selector -- monitor'\\\n\t\t\t\t'*'{-g,--rectangle}'[Set the rectangle of the selected monitor]:WxH+X+Y:( )'\\\n\t\t\t\t'*'{-n,--rename}'[Rename the selected monitor]: :( )'\\\n\t\t\t\t'*'{-o,--reorder-desktops}'[Reorder the desktops of the selected monitor]:*:reorder desktops:_bspc_query_names -- desktops -D'\\\n\t\t\t\t'*'{-r,--remove}'[Remove the selected monitor]'\\\n\t\t\t\t'*'{-s,--swap}'[Swap the selected monitor with the given monitor]: :_bspc_selector -- monitor'\n\t\t\t;;\n\t\t(query)\n\t\t\tlocal -a cmds_no_names=('-T' '--tree' '-N' '--nodes')\n\t\t\tlocal -a cmds=($cmds_no_names '-D' '--desktops' '-M' '--monitors')\n\t\t\t_arguments \\\n\t\t\t\t'*'{-d,--desktop}'[Constrain matches to the selected desktop]: :_bspc_selector -- desktop'\\\n\t\t\t\t'*'{-m,--monitor}'[Constrain matches to the selected monitor]: :_bspc_selector -- monitor'\\\n\t\t\t\t'*'{-n,--node}'[Constrain matches to the selected node]: :_bspc_selector -- node'\\\n\t\t\t\t\"($cmds_no_names --names)--names[Print names instead of IDs. Can only be used with -M and -D]\"\\\n\t\t\t\t\"($cmds --names)\"{-N,--nodes}'[List the IDs of the matching nodes]'\\\n\t\t\t\t\"($cmds --names)\"{-T,--tree}'[Print a JSON representation of the matching item]'\\\n\t\t\t\t\"($cmds)\"{-D,--desktops}'[List the IDs (or names) of the matching desktops]'\\\n\t\t\t\t\"($cmds)\"{-M,--monitors}'[List the IDs (or names) of the matching monitors]'\n\t\t\t;;\n\t\t(wm)\n\t\t\t_arguments \\\n\t\t\t\t'*'{-d,--dump-state}'[Dump the current world state on standard output]'\\\n\t\t\t\t'*'{-l,--load-state}'[Load a world state from the given file]:load state from file:_files'\\\n\t\t\t\t'*'{-a,--add-monitor}'[Add a monitor for the given name and rectangle]:add monitor:( )'\\\n\t\t\t\t'*'{-O,--reorder-monitors}'[Reorder the list of monitors to match the given order]:*: :_bspc_query_names -- monitors -M'\\\n\t\t\t\t'*'{-o,--adopt-orphans}'[Manage all the unmanaged windows remaining from a previous session]'\\\n\t\t\t\t'*'{-h,--record-history}'[Enable or disable the recording of node focus history]:history:(on off)'\\\n\t\t\t\t'*'{-g,--get-status}'[Print the current status information]'\\\n\t\t\t\t'*'{-r,--restart}'[Restart the window manager]'\n\t\t\t;;\n\t\t(subscribe)\n\t\t\tif [[ \"$words[CURRENT-1]\" != (-c|--count) ]] ;then\n\t\t\t\t_values -w \"options\" \\\n\t\t\t\t\t'(-f --fifo)'{-f,--fifo}'[Print a path to a FIFO from which events can be read and return]'\\\n\t\t\t\t\t'(-c --count)'{-c,--count}'[Stop the corresponding bspc process after having received specified count of events]'\n\t\t\t\t_values -w -S \"_\" events all report pointer_action \\\n\t\t\t\t\t\"monitor:: :(add rename remove swap focus geometry)\"\\\n\t\t\t\t\t\"desktop:: :(add rename remove swap transfer focus activate layout)\"\\\n\t\t\t\t\t\"node:: :(add remove swap transfer focus activate presel stack geometry state flag layer)\"\n\t\t\tfi\n\t\t\t;;\n\t\t(rule)\n\t\t\tlocal -a completions by_index\n\t\t\tlocal index=0 target settings class id instance json\n\t\t\t_arguments -C \\\n\t\t\t\t{-a,--add}'[Create a new rule]:*: :->add'\\\n\t\t\t\t{-r,--remove}'[Remove the given rules]:*: :->remove'\\\n\t\t\t\t'(-l --list)'{-l,--list}'[List the rules]'\n\t\t\tcompset -N \"-([ar]|-add|-remove)\"\n\t\t\tcase $state$CURRENT in\n\t\t\t\t(add1)\n\t\t\t\t\tcompset -P '*:'\n\t\t\t\t\tbspc query -N -n '.window' 2> /dev/null |\n\t\t\t\t\t\twhile read id ; do\n\t\t\t\t\t\t\tjson=$(bspc query -T -n $id 2>/dev/null) || continue\n\t\t\t\t\t\t\t[[ \"$json[1]\" = '{' ]] || continue\n\t\t\t\t\t\t\tclass=${${json##*\\\"className\\\":\\\"}%%\\\",\\\"*}\n\t\t\t\t\t\t\tinstance=${${json##*\\\"instanceName\\\":\\\"}%%\\\",\\\"*}\n\t\t\t\t\t\t\t[[ \"$class[1]\" != '{' && \"$instance[1]\" != '{'  ]] || continue\n\t\t\t\t\t\t\tif [ -n \"$IPREFIX\" ] ;then\n\t\t\t\t\t\t\t\t[[ \"$IPREFIX\" == (\"${class}:\"|('\\'|)'*:') ]] &&\n\t\t\t\t\t\t\t\t\tcompletions[(r)$instance]=$instance\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tclass=${class%%:/\\\\:}\n\t\t\t\t\t\t\t\tcompletions[(r)$class]=\"$class\"\n\t\t\t\t\t\t\tfi\n\t\t\t\t\t\tdone\n\t\t\t\t\t;;\n\t\t\t\t(add*)\n\t\t\t\t\t_values -w 'add rule' {border,focus,follow,manage,center}': :(on off)'\\\n\t\t\t\t\t\t'(--one-shot)-o'\\\n\t\t\t\t\t\t'(-o)--one-shot'\\\n\t\t\t\t\t\t'monitor: :_bspc_selector -- monitor'\\\n\t\t\t\t\t\t'desktop: :_bspc_selector -- desktop'\\\n\t\t\t\t\t\t'node: :_bspc_selector -- node'\\\n\t\t\t\t\t\t'rectangle: :( )'\\\n\t\t\t\t\t\t'split_ratio:split ratio:( )'\\\n\t\t\t\t\t\t\"split_dir:split direction:(${dir})\"\\\n\t\t\t\t\t\t\"state:state:(${node_state})\"\\\n\t\t\t\t\t\t\"${flag[@]:#urgent}:set flag:(on off)\"\\\n\t\t\t\t\t\t\"layer:layer:(${layer})\"\n\t\t\t\t\treturn\n\t\t\t\t\t;;\n\t\t\t\t(remove*)\n\t\t\t\t\tcompset -P '*:'\n\t\t\t\t\tbspc rule -l 2> /dev/null |\n\t\t\t\t\t\twhile IFS=\" \" read target settings ;do\n\t\t\t\t\t\t\tby_index+=\"^$((++index)):${target} ${settings}\"\n\t\t\t\t\t\t\tif [ -n \"$IPREFIX\" ] ;then\n\t\t\t\t\t\t\t\tcompletions+=\"${target#*:}\"\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcompletions+=\"${target%:*}\"\n\t\t\t\t\t\t\tfi\n\t\t\t\t\t\tdone\n\t\t\t\t\t[[ -z \"$IPREFIX\" ]] &&\n\t\t\t\t\t\t_describe 'remove rule by position' '(head tail)' -J by_index -- by_index -J by_index\n\t\t\t\t\t;;\n\t\t\t\t(*)\n\t\t\t\t\treturn\n\t\t\t\t\t;;\n\t\t\tesac\n\t\t\tcompletions[(r)\\*]=*\n\t\t\t[ -n \"$IPREFIX\" ] &&\n\t\t\t\t_describe 'match window instance' completions ||\n\t\t\t\t_describe 'match window class' completions -q -S ':'\n\t\t\t;;\n\t\t(config)\n\t\t\tlocal -a {look,behaviour,input}{_bool,}\n\t\t\tlook_bool=(presel_feedback borderless_monocle gapless_monocle borderless_singleton)\n\t\t\tlook=({normal,active,focused}_border_color {top,right,bottom,left}_padding {top,right,bottom,left}_monocle_padding presel_feedback_color border_width window_gap)\n\t\t\tbehaviour_bool=(single_monocle removal_adjustment ignore_ewmh_focus ignore_ewmh_struts center_pseudo_tiled honor_size_hints remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors)\n\t\t\tbehaviour=(mapping_events_count ignore_ewmh_fullscreen external_rules_command split_ratio automatic_scheme initial_polarity directional_focus_tightness status_prefix)\n\t\t\tinput_bool=(swallow_first_click focus_follows_pointer pointer_follows_{focus,monitor})\n\t\t\tinput=(click_to_focus pointer_motion_interval pointer_modifier pointer_action{1,2,3})\n\t\t\tif [[ \"$CURRENT\" == (2|3) ]];then\n\t\t\t\t_arguments \\\n\t\t\t\t\t'-d[Set settings for the selected desktop]: :_bspc_selector -- desktop'\\\n\t\t\t\t\t'-m[Set settings for the selected monitor]: :_bspc_selector -- monitor'\\\n\t\t\t\t\t'-n[Set settings for the selected node]: :_bspc_selector -- node'\n\t\t\tfi\n\t\t\tif [[ \"${words[2]}\" == -* ]] ;then\n\t\t\t\t(( CURRENT == 3 )) && return\n\t\t\t\tif (( CURRENT > 3 )) ;then\n\t\t\t\t\tcompset -n 3\n\t\t\t\tfi\n\t\t\tfi\n\t\t\tif ((CURRENT==2)) ;then\n\t\t\t\t_describe 'look' look -J look -- look_bool -J look\n\t\t\t\t_describe 'input' input -J input -- input_bool -J input\n\t\t\t\t_describe 'behaviour' behaviour -J behaviour -- behaviour_bool -J behaviour\n\t\t\telif ((CURRENT==3)) ;then\n\t\t\t\tsetting=$words[2]\n\t\t\t\tcase $setting in\n\t\t\t\t\t(ignore_ewmh_fullscreen)\n\t\t\t\t\t\t_values -S \",\" \"set $setting\" all none \"enter:: :(exit)\" \"exit:: :(enter)\"\n\t\t\t\t\t\t;;\n\t\t\t\t\t(initial_polarity)\n\t\t\t\t\t\t_values \"set $setting\" first_child second_child\n\t\t\t\t\t\t;;\n\t\t\t\t\t(pointer_action(1|2|3))\n\t\t\t\t\t\t_values \"set $setting\" move resize_side resize_corner focus none\n\t\t\t\t\t\t;;\n\t\t\t\t\t(pointer_modifier)\n\t\t\t\t\t\t_values \"set $setting\" shift control lock mod1 mod2 mod3 mod4 mod5\n\t\t\t\t\t\t;;\n\t\t\t\t\t(directional_focus_tightness)\n\t\t\t\t\t\t_values \"set $setting\" low high\n\t\t\t\t\t\t;;\n\t\t\t\t\t(click_to_focus)\n\t\t\t\t\t\t_values \"set $setting\" any button1 button2 button3 none\n\t\t\t\t\t\t;;\n\t\t\t\t\t(*)\n\t\t\t\t\t\t[[ -n $look_bool[(r)$setting] ]] ||\n\t\t\t\t\t\t\t[[ -n $behaviour_bool[(r)$setting] ]] ||\n\t\t\t\t\t\t\t[[ -n $input_bool[(r)$setting] ]] &&\n\t\t\t\t\t\t\t_values \"set $setting\" true false\n\t\t\t\t\t\t;;\n\t\t\t\tesac\n\t\t\tfi\n\t\t\t;;\n\tesac\n}\n\n_bspc \"$@\"\n"
  },
  {
    "path": "doc/CHANGELOG.md",
    "content": "# From 0.9.11 to 0.9.12\n\n- Fix a regression introduced by 6082d8b (#1533).\n\n# From 0.9.10 to 0.9.11\n\n## Fixes\n\n- Set the input focus before unmapping windows (#811).\n- Arrange across all desktops when handling structs (#1165).\n- Don't set the wrong border color when swapping nodes.\n- Honor `pointer_follows_focus` when swapping nodes.\n- Update EWMH's current desktop when adding a desktop (#1179).\n- Initialize the destination location early (#1196).\n- Handle standard output closure last (#1207).\n- Discard colons within references (#1218).\n- Use separate references in `cmd_query` (#1235).\n- `DESKTOP_SEL`: discard hashes within `MONITOR_SEL` (#1267).\n- Discard colons within refs in `desktop_from_desc` (#1278).\n- Propagate the size constraints toward the root.\n- Set `CLOEXEC` on the sockets except when restarting (#1298).\n- Properly update the sticky count in `transfer_*` (#1199).\n- Don't remove non-receptacles in `kill_node` (#1268).\n- Handle empty insertion point in the `node_add` message (#1306).\n- Windows sometimes not appearing (#935).\n- Refocus the focused window when receiving a FOCUS_IN event for root (#1160).\n- Always return `1` when `execvp()` fails (#1393).\n- Avoid unnecessary relayouts for unchanged values (#1415).\n- Account for vacant nodes when adjusting ratios (#1431).\n- Notify subscribers when a desktop's status changes (#1437).\n- Account for border width in configure requests (#863).\n- Unset `pointer_follows_focus` when refocusing the focused node (#1481).\n- Fix segfault caused by non-null-terminated string (#1503).\n\n## Changes\n\n- Allow negated window modifiers to match non-windows (#1232).\n- Handle sticky nodes in `swap_desktops` (#1298).\n- Don't include pointer events in the node mask.\n- Turn `honor_size_hints` into a node setting (#1447).\n- Set `_NET_WM_WINDOW_TYPE` on monitor root window (#1453).\n- Revamp signal handling (#1480).\n- Set the desktop name in the xsession file (#1493).\n\n## Additions\n\n- Add layout modifiers to desktop selectors : `.tiled`, `.monocle`, `.user_tiled`, `.user_monocle`.\n- Add new setting: `borderless_singleton`.\n- Restore the last window state with `node -t ~`.\n- Emit life cycle events for receptacles.\n- Allow setting a node's splitting type (#1291).\n- Allow cycling the splitting type of a node.\n- Add `--print-socket-path` option (#1367).\n- Allow escaping colons in rule tokenization (#1071).\n\n# From 0.9.9 to 0.9.10\n\n## Additions\n\n- New node descriptor: `first_ancestor`.\n- New node modifiers: `horizontal`, `vertical`.\n\n## Changes\n\n- The node descriptors `next` and `prev` might now return any node. The previous behavior can be emulated by appending `.!hidden.window`.\n- The node descriptors `pointed`, `biggest` and `smallest` now return leaves (in particular `pointed` will now return the *id* of a pointed receptacle). The previous behavior can be emulated by appending `.window`.\n- The *query* command now handles all the possible descriptor-free constraints (for example, `query -N -d .active` now works as expected).\n- The rules can now match against the window's names (`WM_NAME`).\n- The configuration script now receives an argument to indicate whether is was executed after a restart or not.\n- The *intermediate consequences* passed to the external rules command are now in resolved form to avoid unwanted code execution.\n\n# From 0.9.8 to 0.9.9\n\n- Fix a memory allocation bug in the implementation of `wm --restart`.\n- Honor `single_monocle` when the `hidden` flag is toggled.\n\n# From 0.9.7 to 0.9.8\n\n- Fix a potential infinite loop.\n- Fix two bugs having to do with `single_monocle`.\n- Honor `removal_adjustment` for the spiral automatic insertion scheme.\n\n# From 0.9.6 to 0.9.7\n\nThis release fixes a bug in the behavior of `single_monocle`.\n\n# From 0.9.4 to 0.9.6\n\n## Additions\n\n- New *wm* command: `--restart`. It was already possible to restart `bspwm` without loosing the current state through `--{dump,load}-state`, but this command will also keep the existing subscribers intact.\n- New settings: `automatic_scheme`, `removal_adjustment`. The automatic insertion mode now provides three ways of inserting a new node: `spiral`, `longest_side` (the default) and `alternate`. Those schemes are described in the README.\n- New settings: `ignore_ewmh_struts`, `presel_feedback`, `{top,right,bottom,left}_monocle_padding`.\n- New node descriptor: `smallest`.\n- New desktop modifier: `active`.\n\n## Changes\n\n- The `focused` and `active` modifiers now mean the same thing across every object.\n- Fullscreen windows are no longer sent to the `above` layer. Within the same layer, fullscreen windows are now above floating windows. If you want a floating window to be above a fullscreen window, you'll need to rely on layers.\n- Pseudo-tiled windows now shrink automatically.\n\n## Removals\n\n- The `paddingless_monocle` setting was removed (and subsumed). The effect of `paddingless_monocle` can now be achieved with:\n```shell\nfor side in top right bottom left; do\n\tbspc config ${side}_monocle_padding -$(bspc config ${side}_padding)\ndone\n```\n\n# From 0.9.3 to 0.9.4\n\n## Changes\n\n- The following events: `node_{manage,unmanage}` are now `node_{add,remove}`.\n\n## Additions\n\n- New monitor/desktop/node descriptors: `any`, `newest`.\n- New node flag: `marked`.\n- New monitor descriptor: `pointed`.\n- New *wm* command: `--reorder-monitors`.\n- Receptacles are now described in the manual.\n- New `--follow` option added to `node -{m,d,n,s}` and `desktop -{m,s}`.\n- The *subscribe* command now has the following options: `--fifo`, `--count`.\n- New settings: `ignore_ewmh_fullscreen`, `mapping_events_count`.\n\n# From 0.9.2 to 0.9.3\n\n## Changes\n\n- *click_to_focus* is now a button name. Specifying a boolean is deprecated but will still work (`true` is equivalent to `button1`).\n\n## Additions\n\n- `node -r` now accepts a relative fraction argument.\n- An option was added to `query -{M,D,N}` in order to output names instead of IDs: `--names`.\n- New rule consequence: `rectangle=WxH+X+Y`.\n- New settings: `swallow_first_click` and `directional_focus_tightness`.\n\n# From 0.9.1 to 0.9.2\n\n## Changes\n\n- Monitors, desktops and nodes have unique IDs, `bspc query -{N,D,M}` returns IDs and events reference objects by ID instead of name.\n- `bspc` fails verbosely and only returns a single non-zero exit code.\n- The `DIR` descriptor is based on [right-window](https://github.com/ntrrgc/right-window).\n- The `CYCLE_DIR` descriptor isn't limited to the current desktop/monitor anymore. (You can emulate the previous behavior by appending a `.local` modifier to the selector.)\n- `bspc query -{N,D,M}` accepts an optional reference argument used by certain descriptors/modifiers.\n- Monitors are ordered visually by default.\n- The following settings: `border_width`, `window_gap` and `*_padding` behave as expected.\n- External rules also receives the monitor, desktop and node selectors computed from the built-in rules stage as subsequent arguments.\n- The `focus_follows_pointer` setting is implemented via enter notify events.\n\n## Additions\n\n- Nodes can be hidden/shown via the new `hidden` flag.\n- Node receptacles can be inserted with `node -i`. An example is given in `git show e8aa679`.\n- Non-tiled nodes can be moved/resized via `node -{v,z}`.\n- The reference of a selector can be set via the `{NODE,DESKTOP,MONITOR}_SEL#` prefix, example: `bspc node 0x0080000c#south -c` will close the node at the south of `0x0080000c`.\n- Node descriptors: `<node_id>`, `pointed`.\n- Node modifiers: `hidden`, `descendant_of`, `ancestor_of`, `window`, `active`. Example: `bspc query -N 0x00400006 -n .descendant_of` returns the descendants of `0x00400006`.\n- Desktop descriptor: `<desktop_id>`.\n- Monitor descriptor: `<monitor_id>`.\n- Settings: `pointer_motion_interval`, `pointer_modifier`, `pointer_action{1,2,3}`, `click_to_focus`, `honor_size_hints`.\n- Event: `pointer_action`.\n- ICCCM/EWMH atoms: `WM_STATE`, `_NET_WM_STRUT_PARTIAL`.\n- `bspc` shell completions for `fish`.\n\n## Removals\n\n- The `pointer` domain. Pointer actions are handled internally. You need to remove any binding that uses this domain from your `sxhkdrc`.\n- Settings: `history_aware_focus`, `focus_by_distance`. Both settings are merged into the new `DIR` implementation.\n- `monitor -r|--remove-desktops`: use `desktop -r|--remove` instead.\n- `wm -r|--remove-monitor`: use `monitor -r|--remove` instead.\n\n# From 0.9 to 0.9.1\n\n## Overview\n\nAll the commands that acted on leaves can now be applied on internal nodes (including focusing and preselection). Consequently, the *window* domain is now a *node* domain. Please note that some commands are applied to the leaves of the tree rooted at the selected node and not to the node itself.\n\n## Changes\n\n- All the commands that started with `window` now start with `node`.\n- `-W|--windows`, `-w|--window`, `-w|--to-window` are now `-N|--nodes`, `-n|--node`, `-n|--to-node`.\n- We now use cardinal directions: `west,south,north,east` instead of `left,down,up,right` (in fact the latter is just plain wrong: the `up,down` axis is perpendicular to the screen).\n- The `WINDOW_SEL` becomes `NODE_SEL` and now contains a `PATH` specifier to select internal nodes.\n- The `control` domain is renamed to `wm`.\n- `restore -{T,H,S}` was unified into `wm -l|--load-state` and `query -{T,H,S}` into `wm -d|--dump-state`.\n- `control --subscribe` becomes `subscribe`.\n- `node --toggle` (previously `window --toggle`) is split into `node --state` and `node --flag`.\n- The preselection direction (resp. ratio) is now set with `node --presel-dir|-p` (resp. `node --presel-ratio|-o`).\n- The following desktop commands: `--rotate`, `--flip`, `--balance`, `--equalize`, `--circulate` are now node commands.\n- `query -T ...` outputs JSON.\n- `query -{M,D,N}`: the descriptor part of the selector is now optional (e.g.: `query -D -d .urgent`).\n- Many new modifiers were added, some were renamed. The opposite of a modifier is now expressed with the `!` prefix (e.g.: `like` becomes `same_class`, `unlike` becomes `!same_class`, etc.).\n- Modifiers can now be applied to any descriptor (e.g.: `query -N -n 0x80000d.floating`).\n- `wm -l` (previously `restore -T`) will now destroy the existing tree and restore from scratch instead of relying on existing monitors and desktops.\n- `subscribe` (previously `control --subscribe`) now accepts arguments and can receive numerous events from different domains (see the *EVENTS* section of the manual).\n- `rule -a`: it is now possible to specify the class name *and* instance name (e.g.: `rule -a Foo:bar`).\n- `presel_border_color` is now `presel_feedback_color`.\n- `bspwm -v` yields an accurate version.\n- The monitors are sorted, by default, according to the natural visual hierarchy.\n\n## Additions\n\n### Settings\n\n- `single_monocle`.\n- `paddingless_monocle`.\n\n### Commands\n\n- `{node,desktop} --activate`.\n- `node --layer`.\n- `desktop --bubble`.\n- `wm {--add-monitor,--remove-monitor}`.\n- `monitor --rectangle`.\n\n## Removals\n\n### Commands\n\n- `desktop --toggle`\n- `desktop --cancel-presel`\n- `control --toggle-visibility`.\n\n### Settings\n\n- `apply_floating_atom`.\n- `auto_alternate`.\n- `auto_cancel`.\n- `focused_locked_border_color`\n- `active_locked_border_color`\n- `normal_locked_border_color`\n- `focused_sticky_border_color`\n- `active_sticky_border_color`\n- `normal_sticky_border_color`\n- `focused_private_border_color`\n- `active_private_border_color`\n- `normal_private_border_color`\n- `urgent_border_color`\n\n## Message Translation Guide\n\n0.9                                      | 0.9.1\n-----------------------------------------|----------------------------------\n`{left,down,up,right}`                   | `{west,south,north,east}`\n`window -r`                              | `node -o` (`node -r` also exists)\n`window -e DIR RATIO`                    | `node @DIR -r RATIO`\n`window -R DIR DEG`                      | `node @DIR -R DEG`\n`window -w`                              | `node -n`\n`desktop DESKTOP_SEL -R DEG`             | `node @DESKTOP_SEL:/ -R DEG`\n`desktop DESKTOP_SEL -E`                 | `node @DESKTOP_SEL:/ -E`\n`desktop DESKTOP_SEL -B`                 | `node @DESKTOP_SEL:/ -B`\n`desktop DESKTOP_SEL -C forward|backward`| `node @DESKTOP_SEL:/ -C forward|backward`\n`desktop DESKTOP_SEL --cancel-presel`    | `bspc query -N -d DESKTOP_SEL | xargs -I id -n 1 bspc node id -p cancel`\n`window -t floating`                     | `node -t ~floating`\n`query -W -w`                            | `query -N -n .leaf`\n`query -{T,H,S}`                         | `wm -d`\n`restore -{T,H,S}`                       | `wm -l`\n"
  },
  {
    "path": "doc/CONTRIBUTING.md",
    "content": "## Issues\n\nAlways provide the following information when submitting an issue:\n- Output of `bspwm -v`.\n- Content of `bspwmrc`.\n- Steps to reproduce the problem.\n\n## Pull Requests\n\n### Requirements\n\nYou must be comfortable with [C][1], [XCB][2] and [Git][3].\n\n### Coding Style\n\nI follow the [Linux Coding Style][4] with the following variations:\n- [Indent with tabs, align with spaces][5].\n- Always use braces when using control structures.\n\nAn [EditorConfig][6] is included for convinience.\n\n[1]: https://www.bell-labs.com/usr/dmr/www/cbook/\n[2]: https://xcb.freedesktop.org/tutorial/\n[3]: http://git-scm.com/documentation\n[4]: https://www.kernel.org/doc/Documentation/process/coding-style.rst\n[5]: http://lea.verou.me/2012/01/why-tabs-are-clearly-superior/\n[6]: https://editorconfig.org\n\n## Donations\n\n[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RHTYMMB9SHP68)\n"
  },
  {
    "path": "doc/INSTALL.md",
    "content": "# Dependencies\n\n- libxcb\n- xcb-util\n- xcb-util-wm\n\n# Installation\n\n\t$ make\n\t# make install\n\n# Removal\n\n\t# make uninstall\n\n# Packages\n\n- Arch Linux\n\t- [bspwm-git](https://aur.archlinux.org/packages/bspwm-git)\n\t- [bspwm (x86_64)](https://www.archlinux.org/packages/community/x86_64/bspwm)\n\t- [bspwm (i686)](https://www.archlinux.org/packages/community/i686/bspwm)\n\n- Debian\n\t- [bspwm](https://tracker.debian.org/pkg/bspwm)\n\t- [wiki page](https://wiki.debian.org/bspwm)\n\n- Gentoo Linux\n\t- [bspwm](https://packages.gentoo.org/packages/x11-wm/bspwm)\n\t- [bspwm-git](https://github.com/milomouse/ebuilds)\n\n- [FreeBSD](https://www.freshports.org/x11-wm/bspwm)\n\n- [Void Linux](https://github.com/voidlinux/documentation/wiki/bspwm)\n\n- [BunsenLabs](https://forums.bunsenlabs.org/viewtopic.php?id=567)\n\n- [Manjaro Linux](https://forum.manjaro.org/index.php?topic=16994.0)\n\n- [Chakra](https://chakraos.org/ccr/packages.php?ID=6537)\n\n- [Exherbo](http://git.exherbo.org/summer/packages/x11-wm/bspwm)\n"
  },
  {
    "path": "doc/MISC.md",
    "content": "# Mathematical background\n\nThe main data structure is a full binary tree.\n\nA binary tree is *full* if each of its node has either two or zero children.\n\nIf a node has two children it is an internal node, otherwise a leaf.\n\nFundamental theorem:\nLet I be the number of internal nodes and L the number of leaves, then:\n\tL = I + 1\n\n(It can be proved by induction on the number of internal nodes.)\n\nThis means that when we add a leaf to the tree (when a window is created), we must also add one internal node.\n"
  },
  {
    "path": "doc/TODO.md",
    "content": "- Add zoom feature (view point distinct from root).\n- Use BSD `sys/{queue/tree}.h` for {list,tree} structures?\n"
  },
  {
    "path": "doc/bspwm.1",
    "content": "'\\\" t\n.\\\"     Title: bspwm\n.\\\"    Author: [see the \"Author\" section]\n.\\\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>\n.\\\"      Date: 10/08/2025\n.\\\"    Manual: Bspwm Manual\n.\\\"    Source: Bspwm 0.9.12\n.\\\"  Language: English\n.\\\"\n.TH \"BSPWM\" \"1\" \"10/08/2025\" \"Bspwm 0\\&.9\\&.12\" \"Bspwm Manual\"\n.\\\" -----------------------------------------------------------------\n.\\\" * Define some portability stuff\n.\\\" -----------------------------------------------------------------\n.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n.\\\" http://bugs.debian.org/507673\n.\\\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html\n.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.\\\" * set default formatting\n.\\\" -----------------------------------------------------------------\n.\\\" disable hyphenation\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.\\\" * MAIN CONTENT STARTS HERE *\n.\\\" -----------------------------------------------------------------\n.SH \"NAME\"\nbspwm \\- Binary space partitioning window manager\n.SH \"SYNOPSIS\"\n.sp\n\\fBbspwm\\fR [\\fB\\-h\\fR|\\fB\\-v\\fR|\\fB\\-c\\fR \\fICONFIG_PATH\\fR]\n.sp\n\\fBbspc \\-\\-print\\-socket\\-path\\fR\n.sp\n\\fBbspc\\fR \\fIDOMAIN\\fR [\\fISELECTOR\\fR] \\fICOMMANDS\\fR\n.sp\n\\fBbspc\\fR \\fICOMMAND\\fR [\\fIOPTIONS\\fR] [\\fIARGUMENTS\\fR]\n.SH \"DESCRIPTION\"\n.sp\n\\fBbspwm\\fR is a tiling window manager that represents windows as the leaves of a full binary tree\\&.\n.sp\nIt is controlled and configured via \\fBbspc\\fR\\&.\n.SH \"OPTIONS\"\n.PP\n\\fB\\-h\\fR\n.RS 4\nPrint the synopsis and exit\\&.\n.RE\n.PP\n\\fB\\-v\\fR\n.RS 4\nPrint the version and exit\\&.\n.RE\n.PP\n\\fB\\-c\\fR \\fICONFIG_PATH\\fR\n.RS 4\nUse the given configuration file\\&.\n.RE\n.PP\n\\fB\\-\\-print\\-socket\\-path\\fR\n.RS 4\nPrint the\n\\fBbspwm\\fR\nsocket path and exit\\&.\n.RE\n.SH \"COMMON DEFINITIONS\"\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nDIR         := north | west | south | east\nCYCLE_DIR   := next | prev\n.fi\n.if n \\{\\\n.RE\n.\\}\n.SH \"SELECTORS\"\n.sp\nSelectors are used to select a target node, desktop, or monitor\\&. A selector can either describe the target relatively or name it globally\\&.\n.sp\nSelectors consist of an optional reference, a descriptor and any number of non\\-conflicting modifiers as follows:\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\n[REFERENCE#]DESCRIPTOR(\\&.MODIFIER)*\n.fi\n.if n \\{\\\n.RE\n.\\}\n.sp\nThe relative targets are computed in relation to the given reference (the default reference value is \\fBfocused\\fR)\\&.\n.sp\nAn exclamation mark can be prepended to any modifier in order to reverse its meaning\\&.\n.sp\nThe following characters cannot be used in monitor or desktop names: \\fB#\\fR, \\fB:\\fR, \\fB\\&.\\fR\\&.\n.sp\nThe special selector \\fB%<name>\\fR can be used to select a monitor or a desktop with an invalid name\\&.\n.SS \"Node\"\n.sp\nSelect a node\\&.\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nNODE_SEL := [NODE_SEL#](DIR|CYCLE_DIR|PATH|any|first_ancestor|last|newest|\n                        older|newer|focused|pointed|biggest|smallest|\n                        <node_id>)[\\&.[!]focused][\\&.[!]active][\\&.[!]automatic][\\&.[!]local]\n                                  [\\&.[!]leaf][\\&.[!]window][\\&.[!]STATE][\\&.[!]FLAG][\\&.[!]LAYER][\\&.[!]SPLIT_TYPE]\n                                  [\\&.[!]same_class][\\&.[!]descendant_of][\\&.[!]ancestor_of]\n\nSTATE := tiled|pseudo_tiled|floating|fullscreen\n\nFLAG := hidden|sticky|private|locked|marked|urgent\n\nLAYER := below|normal|above\n\nSPLIT_TYPE := horizontal|vertical\n\nPATH := @[DESKTOP_SEL:][[/]JUMP](/JUMP)*\n\nJUMP := first|1|second|2|brother|parent|DIR\n.fi\n.if n \\{\\\n.RE\n.\\}\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBDescriptors\\fR\n.RS 4\n.PP\n\\fIDIR\\fR\n.RS 4\nSelects the window in the given (spacial) direction relative to the reference node\\&.\n.RE\n.PP\n\\fICYCLE_DIR\\fR\n.RS 4\nSelects the node in the given (cyclic) direction relative to the reference node within a depth\\-first in\\-order traversal of the tree\\&.\n.RE\n.PP\n\\fIPATH\\fR\n.RS 4\nSelects the node at the given path\\&.\n.RE\n.PP\nany\n.RS 4\nSelects the first node that matches the given selectors\\&.\n.RE\n.PP\nfirst_ancestor\n.RS 4\nSelects the first ancestor of the reference node that matches the given selectors\\&.\n.RE\n.PP\nlast\n.RS 4\nSelects the previously focused node relative to the reference node\\&.\n.RE\n.PP\nnewest\n.RS 4\nSelects the newest node in the history of the focused node\\&.\n.RE\n.PP\nolder\n.RS 4\nSelects the node older than the reference node in the history\\&.\n.RE\n.PP\nnewer\n.RS 4\nSelects the node newer than the reference node in the history\\&.\n.RE\n.PP\nfocused\n.RS 4\nSelects the currently focused node\\&.\n.RE\n.PP\npointed\n.RS 4\nSelects the leaf under the pointer\\&.\n.RE\n.PP\nbiggest\n.RS 4\nSelects the biggest leaf\\&.\n.RE\n.PP\nsmallest\n.RS 4\nSelects the smallest leaf\\&.\n.RE\n.PP\n<node_id>\n.RS 4\nSelects the node with the given ID\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBPath Jumps\\fR\n.RS 4\n.sp\nThe initial node is the focused node (or the root if the path starts with \\fI/\\fR) of the reference desktop (or the selected desktop if the path has a \\fIDESKTOP_SEL\\fR prefix)\\&.\n.PP\n1|first\n.RS 4\nJumps to the first child\\&.\n.RE\n.PP\n2|second\n.RS 4\nJumps to the second child\\&.\n.RE\n.PP\nbrother\n.RS 4\nJumps to the brother node\\&.\n.RE\n.PP\nparent\n.RS 4\nJumps to the parent node\\&.\n.RE\n.PP\n\\fIDIR\\fR\n.RS 4\nJumps to the node holding the edge in the given direction\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBModifiers\\fR\n.RS 4\n.PP\n[!]focused\n.RS 4\nOnly consider the focused node\\&.\n.RE\n.PP\n[!]active\n.RS 4\nOnly consider nodes that are the focused node of their desktop\\&.\n.RE\n.PP\n[!]automatic\n.RS 4\nOnly consider nodes in automatic insertion mode\\&. See also\n\\fB\\-\\-presel\\-dir\\fR\nunder\n\\fBNode\\fR\nin the\n\\fBDOMAINS\\fR\nsection below\\&.\n.RE\n.PP\n[!]local\n.RS 4\nOnly consider nodes in the reference desktop\\&.\n.RE\n.PP\n[!]leaf\n.RS 4\nOnly consider leaf nodes\\&.\n.RE\n.PP\n[!]window\n.RS 4\nOnly consider nodes that hold a window\\&.\n.RE\n.PP\n[!](tiled|pseudo_tiled|floating|fullscreen)\n.RS 4\nOnly consider windows in the given state\\&.\n.RE\n.PP\n[!]same_class\n.RS 4\nOnly consider windows that have the same class as the reference window\\&.\n.RE\n.PP\n[!]descendant_of\n.RS 4\nOnly consider nodes that are descendants of the reference node\\&.\n.RE\n.PP\n[!]ancestor_of\n.RS 4\nOnly consider nodes that are ancestors of the reference node\\&.\n.RE\n.PP\n[!](hidden|sticky|private|locked|marked|urgent)\n.RS 4\nOnly consider windows that have the given flag set\\&.\n.RE\n.PP\n[!](below|normal|above)\n.RS 4\nOnly consider windows in the given layer\\&.\n.RE\n.PP\n[!](horizontal|vertical)\n.RS 4\nOnly consider nodes with the given split type\\&.\n.RE\n.RE\n.SS \"Desktop\"\n.sp\nSelect a desktop\\&.\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nDESKTOP_SEL := [DESKTOP_SEL#](CYCLE_DIR|any|last|newest|older|newer|\n                              [MONITOR_SEL:](focused|^<n>)|\n                              <desktop_id>|<desktop_name>)[\\&.[!]focused][\\&.[!]active]\n                                                          [\\&.[!]occupied][\\&.[!]urgent][\\&.[!]local]\n                                                          [\\&.[!]LAYOUT][\\&.[!]user_LAYOUT]\n\nLAYOUT := tiled|monocle\n.fi\n.if n \\{\\\n.RE\n.\\}\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBDescriptors\\fR\n.RS 4\n.PP\n\\fICYCLE_DIR\\fR\n.RS 4\nSelects the desktop in the given direction relative to the reference desktop\\&.\n.RE\n.PP\nany\n.RS 4\nSelects the first desktop that matches the given selectors\\&.\n.RE\n.PP\nlast\n.RS 4\nSelects the previously focused desktop relative to the reference desktop\\&.\n.RE\n.PP\nnewest\n.RS 4\nSelects the newest desktop in the history of the focused desktops\\&.\n.RE\n.PP\nolder\n.RS 4\nSelects the desktop older than the reference desktop in the history\\&.\n.RE\n.PP\nnewer\n.RS 4\nSelects the desktop newer than the reference desktop in the history\\&.\n.RE\n.PP\nfocused\n.RS 4\nSelects the currently focused desktop\\&.\n.RE\n.PP\n^<n>\n.RS 4\nSelects the nth desktop\\&. If\n\\fBMONITOR_SEL\\fR\nis given, selects the nth desktop on the selected monitor\\&.\n.RE\n.PP\n<desktop_id>\n.RS 4\nSelects the desktop with the given ID\\&.\n.RE\n.PP\n<desktop_name>\n.RS 4\nSelects the desktop with the given name\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBModifiers\\fR\n.RS 4\n.PP\n[!]focused\n.RS 4\nOnly consider the focused desktop\\&.\n.RE\n.PP\n[!]active\n.RS 4\nOnly consider desktops that are the focused desktop of their monitor\\&.\n.RE\n.PP\n[!]occupied\n.RS 4\nOnly consider occupied desktops\\&.\n.RE\n.PP\n[!]urgent\n.RS 4\nOnly consider urgent desktops\\&.\n.RE\n.PP\n[!]local\n.RS 4\nOnly consider desktops inside the reference monitor\\&.\n.RE\n.PP\n[!](tiled|monocle)\n.RS 4\nOnly consider desktops with the given layout\\&.\n.RE\n.PP\n[!](user_tiled|user_monocle)\n.RS 4\nOnly consider desktops which have the given layout as userLayout\\&.\n.RE\n.RE\n.SS \"Monitor\"\n.sp\nSelect a monitor\\&.\n.sp\n.if n \\{\\\n.RS 4\n.\\}\n.nf\nMONITOR_SEL := [MONITOR_SEL#](DIR|CYCLE_DIR|any|last|newest|older|newer|\n                              focused|pointed|primary|^<n>|\n                              <monitor_id>|<monitor_name>)[\\&.[!]focused][\\&.[!]occupied]\n.fi\n.if n \\{\\\n.RE\n.\\}\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBDescriptors\\fR\n.RS 4\n.PP\n\\fIDIR\\fR\n.RS 4\nSelects the monitor in the given (spacial) direction relative to the reference monitor\\&.\n.RE\n.PP\n\\fICYCLE_DIR\\fR\n.RS 4\nSelects the monitor in the given (cyclic) direction relative to the reference monitor\\&.\n.RE\n.PP\nany\n.RS 4\nSelects the first monitor that matches the given selectors\\&.\n.RE\n.PP\nlast\n.RS 4\nSelects the previously focused monitor relative to the reference monitor\\&.\n.RE\n.PP\nnewest\n.RS 4\nSelects the newest monitor in the history of the focused monitors\\&.\n.RE\n.PP\nolder\n.RS 4\nSelects the monitor older than the reference monitor in the history\\&.\n.RE\n.PP\nnewer\n.RS 4\nSelects the monitor newer than the reference monitor in the history\\&.\n.RE\n.PP\nfocused\n.RS 4\nSelects the currently focused monitor\\&.\n.RE\n.PP\npointed\n.RS 4\nSelects the monitor under the pointer\\&.\n.RE\n.PP\nprimary\n.RS 4\nSelects the primary monitor\\&.\n.RE\n.PP\n^<n>\n.RS 4\nSelects the nth monitor\\&.\n.RE\n.PP\n<monitor_id>\n.RS 4\nSelects the monitor with the given ID\\&.\n.RE\n.PP\n<monitor_name>\n.RS 4\nSelects the monitor with the given name\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBModifiers\\fR\n.RS 4\n.PP\n[!]focused\n.RS 4\nOnly consider the focused monitor\\&.\n.RE\n.PP\n[!]occupied\n.RS 4\nOnly consider monitors where the focused desktop is occupied\\&.\n.RE\n.RE\n.SH \"WINDOW STATES\"\n.PP\ntiled\n.RS 4\nIts size and position are determined by the window tree\\&.\n.RE\n.PP\npseudo_tiled\n.RS 4\nA tiled window that automatically shrinks but doesn\\(cqt stretch beyond its floating size\\&.\n.RE\n.PP\nfloating\n.RS 4\nCan be moved/resized freely\\&. Although it doesn\\(cqt use any tiling space, it is still part of the window tree\\&.\n.RE\n.PP\nfullscreen\n.RS 4\nFills its monitor rectangle and has no borders\\&.\n.RE\n.SH \"NODE FLAGS\"\n.PP\nhidden\n.RS 4\nIs hidden and doesn\\(cqt occupy any tiling space\\&.\n.RE\n.PP\nsticky\n.RS 4\nStays in the focused desktop of its monitor\\&.\n.RE\n.PP\nprivate\n.RS 4\nTries to keep the same tiling position/size\\&.\n.RE\n.PP\nlocked\n.RS 4\nIgnores the\n\\fBnode \\-\\-close\\fR\nmessage\\&.\n.RE\n.PP\nmarked\n.RS 4\nIs marked (useful for deferred actions)\\&. A marked node becomes unmarked after being sent on a preselected node\\&.\n.RE\n.PP\nurgent\n.RS 4\nHas its urgency hint set\\&. This flag is set externally\\&.\n.RE\n.SH \"STACKING LAYERS\"\n.sp\nThere\\(cqs three stacking layers: BELOW, NORMAL and ABOVE\\&.\n.sp\nIn each layer, the windows are orderered as follows: tiled & pseudo\\-tiled < floating < fullscreen\\&.\n.SH \"RECEPTACLES\"\n.sp\nA leaf node that doesn\\(cqt hold any window is called a receptacle\\&. When a node is inserted on a receptacle in automatic mode, it will replace the receptacle\\&. A receptacle can be inserted on a node, preselected and killed\\&. Receptacles can therefore be used to build a tree whose leaves are receptacles\\&. Using the appropriate rules, one can then send windows on the leaves of this tree\\&. This feature is used in \\fIexamples/receptacles\\fR to store and recreate layouts\\&.\n.SH \"DOMAINS\"\n.SS \"Node\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\nnode [\\fINODE_SEL\\fR] \\fICOMMANDS\\fR\n.sp\nIf \\fINODE_SEL\\fR is omitted, \\fBfocused\\fR is assumed\\&.\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCommands\\fR\n.RS 4\n.PP\n\\fB\\-f\\fR, \\fB\\-\\-focus\\fR [\\fINODE_SEL\\fR]\n.RS 4\nFocus the selected or given node\\&.\n.RE\n.PP\n\\fB\\-a\\fR, \\fB\\-\\-activate\\fR [\\fINODE_SEL\\fR]\n.RS 4\nActivate the selected or given node\\&.\n.RE\n.PP\n\\fB\\-d\\fR, \\fB\\-\\-to\\-desktop\\fR \\fIDESKTOP_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSend the selected node to the given desktop\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused node will stay focused\\&.\n.RE\n.PP\n\\fB\\-m\\fR, \\fB\\-\\-to\\-monitor\\fR \\fIMONITOR_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSend the selected node to the given monitor\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused node will stay focused\\&.\n.RE\n.PP\n\\fB\\-n\\fR, \\fB\\-\\-to\\-node\\fR \\fINODE_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSend the selected node on the given node\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused node will stay focused\\&.\n.RE\n.PP\n\\fB\\-s\\fR, \\fB\\-\\-swap\\fR \\fINODE_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSwap the selected node with the given node\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused node will stay focused\\&.\n.RE\n.PP\n\\fB\\-p\\fR, \\fB\\-\\-presel\\-dir\\fR [~]\\fIDIR\\fR|cancel\n.RS 4\nPreselect the splitting area of the selected node (or cancel the preselection)\\&. If\n\\fB~\\fR\nis prepended to\n\\fIDIR\\fR\nand the current preselection direction matches\n\\fIDIR\\fR, then the argument is interpreted as\n\\fBcancel\\fR\\&. A node with a preselected area is said to be in \"manual insertion mode\"\\&.\n.RE\n.PP\n\\fB\\-o\\fR, \\fB\\-\\-presel\\-ratio\\fR \\fIRATIO\\fR\n.RS 4\nSet the splitting ratio of the preselection area\\&.\n.RE\n.PP\n\\fB\\-v\\fR, \\fB\\-\\-move\\fR \\fIdx\\fR \\fIdy\\fR\n.RS 4\nMove the selected window by\n\\fIdx\\fR\npixels horizontally and\n\\fIdy\\fR\npixels vertically\\&.\n.RE\n.PP\n\\fB\\-z\\fR, \\fB\\-\\-resize\\fR top|left|bottom|right|top_left|top_right|bottom_right|bottom_left \\fIdx\\fR \\fIdy\\fR\n.RS 4\nResize the selected window by moving the given handle by\n\\fIdx\\fR\npixels horizontally and\n\\fIdy\\fR\npixels vertically\\&.\n.RE\n.PP\n\\fB\\-y\\fR, \\fB\\-\\-type\\fR \\fICYCLE_DIR\\fR|horizontal|vertical\n.RS 4\nSet or cycle the splitting type of the selected node\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-ratio\\fR \\fIRATIO\\fR|(+|\\-)(\\fIPIXELS\\fR|\\fIFRACTION\\fR)\n.RS 4\nSet the splitting ratio of the selected node (0 <\n\\fIRATIO\\fR\n< 1)\\&.\n.RE\n.PP\n\\fB\\-R\\fR, \\fB\\-\\-rotate\\fR \\fI90|270|180\\fR\n.RS 4\nRotate the tree rooted at the selected node\\&.\n.RE\n.PP\n\\fB\\-F\\fR, \\fB\\-\\-flip\\fR \\fIhorizontal|vertical\\fR\n.RS 4\nFlip the tree rooted at selected node\\&.\n.RE\n.PP\n\\fB\\-E\\fR, \\fB\\-\\-equalize\\fR\n.RS 4\nReset the split ratios of the tree rooted at the selected node to their default value\\&.\n.RE\n.PP\n\\fB\\-B\\fR, \\fB\\-\\-balance\\fR\n.RS 4\nAdjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area\\&.\n.RE\n.PP\n\\fB\\-C\\fR, \\fB\\-\\-circulate\\fR forward|backward\n.RS 4\nCirculate the windows of the tree rooted at the selected node\\&.\n.RE\n.PP\n\\fB\\-t\\fR, \\fB\\-\\-state\\fR ~|[~]\\fISTATE\\fR\n.RS 4\nSet the state of the selected window\\&. If\n\\fB~\\fR\nis present and the current state matches\n\\fISTATE\\fR, then the argument is interpreted as its last state\\&. If the argument is just\n\\fB~\\fR\nwith\n\\fISTATE\\fR\nomitted, then the state of the selected window is set to its last state\\&.\n.RE\n.PP\n\\fB\\-g\\fR, \\fB\\-\\-flag\\fR hidden|sticky|private|locked|marked[=on|off]\n.RS 4\nSet or toggle the given flag for the selected node\\&.\n.RE\n.PP\n\\fB\\-l\\fR, \\fB\\-\\-layer\\fR below|normal|above\n.RS 4\nSet the stacking layer of the selected window\\&.\n.RE\n.PP\n\\fB\\-i\\fR, \\fB\\-\\-insert\\-receptacle\\fR\n.RS 4\nInsert a receptacle node at the selected node\\&.\n.RE\n.PP\n\\fB\\-c\\fR, \\fB\\-\\-close\\fR\n.RS 4\nClose the windows rooted at the selected node\\&.\n.RE\n.PP\n\\fB\\-k\\fR, \\fB\\-\\-kill\\fR\n.RS 4\nKill the windows rooted at the selected node\\&.\n.RE\n.RE\n.SS \"Desktop\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\ndesktop [\\fIDESKTOP_SEL\\fR] \\fICOMMANDS\\fR\n.sp\nIf \\fIDESKTOP_SEL\\fR is omitted, \\fBfocused\\fR is assumed\\&.\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCOMMANDS\\fR\n.RS 4\n.PP\n\\fB\\-f\\fR, \\fB\\-\\-focus\\fR [\\fIDESKTOP_SEL\\fR]\n.RS 4\nFocus the selected or given desktop\\&.\n.RE\n.PP\n\\fB\\-a\\fR, \\fB\\-\\-activate\\fR [\\fIDESKTOP_SEL\\fR]\n.RS 4\nActivate the selected or given desktop\\&.\n.RE\n.PP\n\\fB\\-m\\fR, \\fB\\-\\-to\\-monitor\\fR \\fIMONITOR_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSend the selected desktop to the given monitor\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused desktop will stay focused\\&.\n.RE\n.PP\n\\fB\\-s\\fR, \\fB\\-\\-swap\\fR \\fIDESKTOP_SEL\\fR [\\fB\\-\\-follow\\fR]\n.RS 4\nSwap the selected desktop with the given desktop\\&. If\n\\fB\\-\\-follow\\fR\nis passed, the focused desktop will stay focused\\&.\n.RE\n.PP\n\\fB\\-l\\fR, \\fB\\-\\-layout\\fR \\fICYCLE_DIR\\fR|monocle|tiled\n.RS 4\nSet or cycle the layout of the selected desktop\\&.\n.RE\n.PP\n\\fB\\-n\\fR, \\fB\\-\\-rename\\fR <new_name>\n.RS 4\nRename the selected desktop\\&.\n.RE\n.PP\n\\fB\\-b\\fR, \\fB\\-\\-bubble\\fR \\fICYCLE_DIR\\fR\n.RS 4\nBubble the selected desktop in the given direction\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-remove\\fR\n.RS 4\nRemove the selected desktop\\&.\n.RE\n.RE\n.SS \"Monitor\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\nmonitor [\\fIMONITOR_SEL\\fR] \\fICOMMANDS\\fR\n.sp\nIf \\fIMONITOR_SEL\\fR is omitted, \\fBfocused\\fR is assumed\\&.\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCommands\\fR\n.RS 4\n.PP\n\\fB\\-f\\fR, \\fB\\-\\-focus\\fR [\\fIMONITOR_SEL\\fR]\n.RS 4\nFocus the selected or given monitor\\&.\n.RE\n.PP\n\\fB\\-s\\fR, \\fB\\-\\-swap\\fR \\fIMONITOR_SEL\\fR\n.RS 4\nSwap the selected monitor with the given monitor\\&.\n.RE\n.PP\n\\fB\\-a\\fR, \\fB\\-\\-add\\-desktops\\fR <name>\\&...\n.RS 4\nCreate desktops with the given names in the selected monitor\\&.\n.RE\n.PP\n\\fB\\-o\\fR, \\fB\\-\\-reorder\\-desktops\\fR <name>\\&...\n.RS 4\nReorder the desktops of the selected monitor to match the given order\\&.\n.RE\n.PP\n\\fB\\-d\\fR, \\fB\\-\\-reset\\-desktops\\fR <name>\\&...\n.RS 4\nRename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops\\&.\n.RE\n.PP\n\\fB\\-g\\fR, \\fB\\-\\-rectangle\\fR WxH+X+Y\n.RS 4\nSet the rectangle of the selected monitor\\&.\n.RE\n.PP\n\\fB\\-n\\fR, \\fB\\-\\-rename\\fR <new_name>\n.RS 4\nRename the selected monitor\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-remove\\fR\n.RS 4\nRemove the selected monitor\\&.\n.RE\n.RE\n.SS \"Query\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\nquery \\fICOMMANDS\\fR [\\fIOPTIONS\\fR]\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCommands\\fR\n.RS 4\n.sp\nThe optional selectors are references\\&.\n.PP\n\\fB\\-N\\fR, \\fB\\-\\-nodes\\fR [\\fINODE_SEL\\fR]\n.RS 4\nList the IDs of the matching nodes\\&.\n.RE\n.PP\n\\fB\\-D\\fR, \\fB\\-\\-desktops\\fR [\\fIDESKTOP_SEL\\fR]\n.RS 4\nList the IDs (or names) of the matching desktops\\&.\n.RE\n.PP\n\\fB\\-M\\fR, \\fB\\-\\-monitors\\fR [\\fIMONITOR_SEL\\fR]\n.RS 4\nList the IDs (or names) of the matching monitors\\&.\n.RE\n.PP\n\\fB\\-T\\fR, \\fB\\-\\-tree\\fR\n.RS 4\nPrint a JSON representation of the matching item\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBOptions\\fR\n.RS 4\n.PP\n\\fB\\-m\\fR,\\fB\\-\\-monitor\\fR [\\fIMONITOR_SEL\\fR|\\fIMONITOR_MODIFIERS\\fR], \\fB\\-d\\fR,\\fB\\-\\-desktop\\fR [\\fIDESKTOP_SEL\\fR|\\fIDESKTOP_MODIFIERS\\fR], \\fB\\-n\\fR,\\fB\\-\\-node\\fR [\\fINODE_SEL\\fR|\\fINODE_MODIFIERS\\fR]\n.RS 4\nConstrain matches to the selected monitors, desktops or nodes\\&.\n.RE\n.PP\n\\fB\\-\\-names\\fR\n.RS 4\nPrint names instead of IDs\\&. Can only be used with\n\\fI\\-M\\fR\nand\n\\fI\\-D\\fR\\&.\n.RE\n.RE\n.SS \"Wm\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\nwm \\fICOMMANDS\\fR\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCommands\\fR\n.RS 4\n.PP\n\\fB\\-d\\fR, \\fB\\-\\-dump\\-state\\fR\n.RS 4\nDump the current world state on standard output\\&.\n.RE\n.PP\n\\fB\\-l\\fR, \\fB\\-\\-load\\-state\\fR <file_path>\n.RS 4\nLoad a world state from the given file\\&. The path must be absolute\\&.\n.RE\n.PP\n\\fB\\-a\\fR, \\fB\\-\\-add\\-monitor\\fR <name> WxH+X+Y\n.RS 4\nAdd a monitor for the given name and rectangle\\&.\n.RE\n.PP\n\\fB\\-O\\fR, \\fB\\-\\-reorder\\-monitors\\fR <name>\\&...\n.RS 4\nReorder the list of monitors to match the given order\\&.\n.RE\n.PP\n\\fB\\-o\\fR, \\fB\\-\\-adopt\\-orphans\\fR\n.RS 4\nManage all the unmanaged windows remaining from a previous session\\&.\n.RE\n.PP\n\\fB\\-h\\fR, \\fB\\-\\-record\\-history\\fR on|off\n.RS 4\nEnable or disable the recording of node focus history\\&.\n.RE\n.PP\n\\fB\\-g\\fR, \\fB\\-\\-get\\-status\\fR\n.RS 4\nPrint the current status information\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-restart\\fR\n.RS 4\nRestart the window manager\n.RE\n.RE\n.SS \"Rule\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.sp\nrule \\fICOMMANDS\\fR\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBCommands\\fR\n.RS 4\n.PP\n\\fB\\-a\\fR, \\fB\\-\\-add\\fR (<class_name>|*)[:(<instance_name>|*)[:(<name>|*)]] [\\fB\\-o\\fR|\\fB\\-\\-one\\-shot\\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [honor_size_hints=(true|false|tiled|floating)] [split_dir=DIR] [split_ratio=RATIO] [(hidden|sticky|private|locked|marked|center|follow|manage|focus|border)=(on|off)] [rectangle=WxH+X+Y]\n.RS 4\nCreate a new rule\\&. Colons in the\n\\fIinstance_name\\fR,\n\\fIclass_name\\fR, or\n\\fIname\\fR\nfields can be escaped with a backslash\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-remove\\fR ^<n>|head|tail|(<class_name>|*)[:(<instance_name>|*)[:(<name>|*)]]\\&...\n.RS 4\nRemove the given rules\\&.\n.RE\n.PP\n\\fB\\-l\\fR, \\fB\\-\\-list\\fR\n.RS 4\nList the rules\\&.\n.RE\n.RE\n.SS \"Config\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.PP\nconfig [\\-m \\fIMONITOR_SEL\\fR|\\-d \\fIDESKTOP_SEL\\fR|\\-n \\fINODE_SEL\\fR] <setting> [<value>]\n.RS 4\nGet or set the value of <setting>\\&.\n.RE\n.RE\n.SS \"Subscribe\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.PP\nsubscribe [\\fIOPTIONS\\fR] (all|report|monitor|desktop|node|\\&...)*\n.RS 4\nContinuously print events\\&. See the\n\\fBEVENTS\\fR\nsection for the description of each event\\&.\n.RE\n.RE\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBOptions\\fR\n.RS 4\n.PP\n\\fB\\-f\\fR, \\fB\\-\\-fifo\\fR\n.RS 4\nPrint a path to a FIFO from which events can be read and return\\&.\n.RE\n.PP\n\\fB\\-c\\fR, \\fB\\-\\-count\\fR \\fICOUNT\\fR\n.RS 4\nStop the corresponding\n\\fBbspc\\fR\nprocess after having received\n\\fICOUNT\\fR\nevents\\&.\n.RE\n.RE\n.SS \"Quit\"\n.sp\n.it 1 an-trap\n.nr an-no-space-flag 1\n.nr an-break-flag 1\n.br\n.ps +1\n\\fBGeneral Syntax\\fR\n.RS 4\n.PP\nquit [<status>]\n.RS 4\nQuit with an optional exit status\\&.\n.RE\n.RE\n.SH \"EXIT CODES\"\n.sp\nIf the server can\\(cqt handle a message, \\fBbspc\\fR will return with a non\\-zero exit code\\&.\n.SH \"SETTINGS\"\n.sp\nColors are in the form \\fI#RRGGBB\\fR, booleans are \\fItrue\\fR, \\fIon\\fR, \\fIfalse\\fR or \\fIoff\\fR\\&.\n.sp\nAll the boolean settings are \\fIfalse\\fR by default unless stated otherwise\\&.\n.SS \"Global Settings\"\n.PP\n\\fInormal_border_color\\fR\n.RS 4\nColor of the border of an unfocused window\\&.\n.RE\n.PP\n\\fIactive_border_color\\fR\n.RS 4\nColor of the border of a focused window of an unfocused monitor\\&.\n.RE\n.PP\n\\fIfocused_border_color\\fR\n.RS 4\nColor of the border of a focused window of a focused monitor\\&.\n.RE\n.PP\n\\fIpresel_feedback_color\\fR\n.RS 4\nColor of the\n\\fBnode \\-\\-presel\\-{dir,ratio}\\fR\nmessage feedback area\\&.\n.RE\n.PP\n\\fIsplit_ratio\\fR\n.RS 4\nDefault split ratio\\&.\n.RE\n.PP\n\\fIstatus_prefix\\fR\n.RS 4\nPrefix prepended to each of the status lines\\&.\n.RE\n.PP\n\\fIexternal_rules_command\\fR\n.RS 4\nAbsolute path to the command used to retrieve rule consequences\\&. The command will receive the following arguments: window ID, class name, instance name, and intermediate consequences\\&. The output of that command must have the following format:\n\\fBkey1=value1 key2=value2 \\&...\\fR\n(the valid key/value pairs are given in the description of the\n\\fIrule\\fR\ncommand)\\&.\n.RE\n.PP\n\\fIautomatic_scheme\\fR\n.RS 4\nThe insertion scheme used when the insertion point is in automatic mode\\&. Accept the following values:\n\\fBlongest_side\\fR,\n\\fBalternate\\fR,\n\\fBspiral\\fR\\&.\n.RE\n.PP\n\\fIinitial_polarity\\fR\n.RS 4\nOn which child should a new window be attached when adding a window on a single window tree in automatic mode\\&. Accept the following values:\n\\fBfirst_child\\fR,\n\\fBsecond_child\\fR\\&.\n.RE\n.PP\n\\fIdirectional_focus_tightness\\fR\n.RS 4\nThe tightness of the algorithm used to decide whether a window is on the\n\\fIDIR\\fR\nside of another window\\&. Accept the following values:\n\\fBhigh\\fR,\n\\fBlow\\fR\\&.\n.RE\n.PP\n\\fIremoval_adjustment\\fR\n.RS 4\nAdjust the brother when unlinking a node from the tree in accordance with the automatic insertion scheme\\&.\n.RE\n.PP\n\\fIpresel_feedback\\fR\n.RS 4\nDraw the preselection feedback area\\&. Defaults to\n\\fItrue\\fR\\&.\n.RE\n.PP\n\\fIborderless_monocle\\fR\n.RS 4\nRemove borders of tiled windows for the\n\\fBmonocle\\fR\ndesktop layout\\&.\n.RE\n.PP\n\\fIgapless_monocle\\fR\n.RS 4\nRemove gaps of tiled windows for the\n\\fBmonocle\\fR\ndesktop layout\\&.\n.RE\n.PP\n\\fItop_monocle_padding\\fR, \\fIright_monocle_padding\\fR, \\fIbottom_monocle_padding\\fR, \\fIleft_monocle_padding\\fR\n.RS 4\nPadding space added at the sides of the screen for the\n\\fBmonocle\\fR\ndesktop layout\\&.\n.RE\n.PP\n\\fIsingle_monocle\\fR\n.RS 4\nSet the desktop layout to\n\\fBmonocle\\fR\nif there\\(cqs only one tiled window in the tree\\&.\n.RE\n.PP\n\\fIborderless_singleton\\fR\n.RS 4\nRemove borders of the only window on the only monitor regardless its layout\\&.\n.RE\n.PP\n\\fIpointer_motion_interval\\fR\n.RS 4\nThe minimum interval, in milliseconds, between two motion notify events\\&.\n.RE\n.PP\n\\fIpointer_modifier\\fR\n.RS 4\nKeyboard modifier used for moving or resizing windows\\&. Accept the following values:\n\\fBshift\\fR,\n\\fBcontrol\\fR,\n\\fBlock\\fR,\n\\fBmod1\\fR,\n\\fBmod2\\fR,\n\\fBmod3\\fR,\n\\fBmod4\\fR,\n\\fBmod5\\fR\\&.\n.RE\n.PP\n\\fIpointer_action1\\fR, \\fIpointer_action2\\fR, \\fIpointer_action3\\fR\n.RS 4\nAction performed when pressing\n\\fIpointer_modifier\\fR\n+\n\\fIbutton<n>\\fR\\&. Accept the following values:\n\\fBmove\\fR,\n\\fBresize_side\\fR,\n\\fBresize_corner\\fR,\n\\fBfocus\\fR,\n\\fBnone\\fR\\&.\n.RE\n.PP\n\\fIclick_to_focus\\fR\n.RS 4\nButton used for focusing a window (or a monitor)\\&. The possible values are:\n\\fBbutton1\\fR,\n\\fBbutton2\\fR,\n\\fBbutton3\\fR,\n\\fBany\\fR,\n\\fBnone\\fR\\&. Defaults to\n\\fBbutton1\\fR\\&.\n.RE\n.PP\n\\fIswallow_first_click\\fR\n.RS 4\nDon\\(cqt replay the click that makes a window focused if\n\\fIclick_to_focus\\fR\nisn\\(cqt\n\\fBnone\\fR\\&.\n.RE\n.PP\n\\fIfocus_follows_pointer\\fR\n.RS 4\nFocus the window under the pointer\\&.\n.RE\n.PP\n\\fIpointer_follows_focus\\fR\n.RS 4\nWhen focusing a window, put the pointer at its center\\&.\n.RE\n.PP\n\\fIpointer_follows_monitor\\fR\n.RS 4\nWhen focusing a monitor, put the pointer at its center\\&.\n.RE\n.PP\n\\fImapping_events_count\\fR\n.RS 4\nHandle the next\n\\fBmapping_events_count\\fR\nmapping notify events\\&. A negative value implies that every event needs to be handled\\&.\n.RE\n.PP\n\\fIignore_ewmh_focus\\fR\n.RS 4\nIgnore EWMH focus requests coming from applications\\&.\n.RE\n.PP\n\\fIignore_ewmh_fullscreen\\fR\n.RS 4\nBlock the fullscreen state transitions that originate from an EWMH request\\&. The possible values are:\n\\fBnone\\fR,\n\\fBall\\fR, or a comma separated list of the following values:\n\\fBenter\\fR,\n\\fBexit\\fR\\&.\n.RE\n.PP\n\\fIignore_ewmh_struts\\fR\n.RS 4\nIgnore strut hinting from clients requesting to reserve space (i\\&.e\\&. task bars)\\&.\n.RE\n.PP\n\\fIcenter_pseudo_tiled\\fR\n.RS 4\nCenter pseudo tiled windows into their tiling rectangles\\&. Defaults to\n\\fItrue\\fR\\&.\n.RE\n.PP\n\\fIremove_disabled_monitors\\fR\n.RS 4\nConsider disabled monitors as disconnected\\&.\n.RE\n.PP\n\\fIremove_unplugged_monitors\\fR\n.RS 4\nRemove unplugged monitors\\&.\n.RE\n.PP\n\\fImerge_overlapping_monitors\\fR\n.RS 4\nMerge overlapping monitors (the bigger remains)\\&.\n.RE\n.SS \"Monitor and Desktop Settings\"\n.PP\n\\fItop_padding\\fR, \\fIright_padding\\fR, \\fIbottom_padding\\fR, \\fIleft_padding\\fR\n.RS 4\nPadding space added at the sides of the monitor or desktop\\&.\n.RE\n.SS \"Desktop Settings\"\n.PP\n\\fIwindow_gap\\fR\n.RS 4\nSize of the gap that separates windows\\&.\n.RE\n.SS \"Node Settings\"\n.PP\n\\fIborder_width\\fR\n.RS 4\nWindow border width\\&.\n.RE\n.PP\n\\fIhonor_size_hints\\fR\n.RS 4\nIf\n\\fItrue\\fR, apply ICCCM window size hints to all windows\\&. If\n\\fIfloating\\fR, only apply them to floating and pseudo tiled windows\\&. If\n\\fItiled\\fR, only apply them to tiled windows\\&. If\n\\fIfalse\\fR, don\\(cqt apply them\\&. Defaults to\n\\fIfalse\\fR\\&.\n.RE\n.SH \"POINTER BINDINGS\"\n.PP\n\\fIclick_to_focus\\fR\n.RS 4\nFocus the window (or the monitor) under the pointer if the value isn\\(cqt\n\\fBnone\\fR\\&.\n.RE\n.PP\n\\fIpointer_modifier\\fR + \\fIbutton1\\fR\n.RS 4\nMove the window under the pointer\\&.\n.RE\n.PP\n\\fIpointer_modifier\\fR + \\fIbutton2\\fR\n.RS 4\nResize the window under the pointer by dragging the nearest side\\&.\n.RE\n.PP\n\\fIpointer_modifier\\fR + \\fIbutton3\\fR\n.RS 4\nResize the window under the pointer by dragging the nearest corner\\&.\n.RE\n.sp\nThe behavior of \\fIpointer_modifier\\fR + \\fIbutton<n>\\fR can be modified through the \\fIpointer_action<n>\\fR setting\\&.\n.SH \"EVENTS\"\n.PP\n\\fIreport\\fR\n.RS 4\nSee the next section for the description of the format\\&.\n.RE\n.PP\n\\fImonitor_add <monitor_id> <monitor_name> <monitor_geometry>\\fR\n.RS 4\nA monitor is added\\&.\n.RE\n.PP\n\\fImonitor_rename <monitor_id> <old_name> <new_name>\\fR\n.RS 4\nA monitor is renamed\\&.\n.RE\n.PP\n\\fImonitor_remove <monitor_id>\\fR\n.RS 4\nA monitor is removed\\&.\n.RE\n.PP\n\\fImonitor_swap <src_monitor_id> <dst_monitor_id>\\fR\n.RS 4\nA monitor is swapped\\&.\n.RE\n.PP\n\\fImonitor_focus <monitor_id>\\fR\n.RS 4\nA monitor is focused\\&.\n.RE\n.PP\n\\fImonitor_geometry <monitor_id> <monitor_geometry>\\fR\n.RS 4\nThe geometry of a monitor changed\\&.\n.RE\n.PP\n\\fIdesktop_add <monitor_id> <desktop_id> <desktop_name>\\fR\n.RS 4\nA desktop is added\\&.\n.RE\n.PP\n\\fIdesktop_rename <monitor_id> <desktop_id> <old_name> <new_name>\\fR\n.RS 4\nA desktop is renamed\\&.\n.RE\n.PP\n\\fIdesktop_remove <monitor_id> <desktop_id>\\fR\n.RS 4\nA desktop is removed\\&.\n.RE\n.PP\n\\fIdesktop_swap <src_monitor_id> <src_desktop_id> <dst_monitor_id> <dst_desktop_id>\\fR\n.RS 4\nA desktop is swapped\\&.\n.RE\n.PP\n\\fIdesktop_transfer <src_monitor_id> <src_desktop_id> <dst_monitor_id>\\fR\n.RS 4\nA desktop is transferred\\&.\n.RE\n.PP\n\\fIdesktop_focus <monitor_id> <desktop_id>\\fR\n.RS 4\nA desktop is focused\\&.\n.RE\n.PP\n\\fIdesktop_activate <monitor_id> <desktop_id>\\fR\n.RS 4\nA desktop is activated\\&.\n.RE\n.PP\n\\fIdesktop_layout <monitor_id> <desktop_id> tiled|monocle\\fR\n.RS 4\nThe layout of a desktop changed\\&.\n.RE\n.PP\n\\fInode_add <monitor_id> <desktop_id> <ip_id> <node_id>\\fR\n.RS 4\nA node is added\\&.\n.RE\n.PP\n\\fInode_remove <monitor_id> <desktop_id> <node_id>\\fR\n.RS 4\nA node is removed\\&.\n.RE\n.PP\n\\fInode_swap <src_monitor_id> <src_desktop_id> <src_node_id> <dst_monitor_id> <dst_desktop_id> <dst_node_id>\\fR\n.RS 4\nA node is swapped\\&.\n.RE\n.PP\n\\fInode_transfer <src_monitor_id> <src_desktop_id> <src_node_id> <dst_monitor_id> <dst_desktop_id> <dst_node_id>\\fR\n.RS 4\nA node is transferred\\&.\n.RE\n.PP\n\\fInode_focus <monitor_id> <desktop_id> <node_id>\\fR\n.RS 4\nA node is focused\\&.\n.RE\n.PP\n\\fInode_activate <monitor_id> <desktop_id> <node_id>\\fR\n.RS 4\nA node is activated\\&.\n.RE\n.PP\n\\fInode_presel <monitor_id> <desktop_id> <node_id> (dir DIR|ratio RATIO|cancel)\\fR\n.RS 4\nA node is preselected\\&.\n.RE\n.PP\n\\fInode_stack <node_id_1> below|above <node_id_2>\\fR\n.RS 4\nA node is stacked below or above another node\\&.\n.RE\n.PP\n\\fInode_geometry <monitor_id> <desktop_id> <node_id> <node_geometry>\\fR\n.RS 4\nThe geometry of a window changed\\&.\n.RE\n.PP\n\\fInode_state <monitor_id> <desktop_id> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off\\fR\n.RS 4\nThe state of a window changed\\&.\n.RE\n.PP\n\\fInode_flag <monitor_id> <desktop_id> <node_id> hidden|sticky|private|locked|marked|urgent on|off\\fR\n.RS 4\nOne of the flags of a node changed\\&.\n.RE\n.PP\n\\fInode_layer <monitor_id> <desktop_id> <node_id> below|normal|above\\fR\n.RS 4\nThe layer of a window changed\\&.\n.RE\n.PP\n\\fIpointer_action <monitor_id> <desktop_id> <node_id> move|resize_corner|resize_side begin|end\\fR\n.RS 4\nA pointer action occurred\\&.\n.RE\n.sp\nPlease note that \\fBbspwm\\fR initializes monitors before it reads messages on its socket, therefore the initial monitor events can\\(cqt be received\\&.\n.SH \"REPORT FORMAT\"\n.sp\nEach report event message is composed of items separated by colons\\&.\n.sp\nEach item has the form \\fI<type><value>\\fR where \\fI<type>\\fR is the first character of the item\\&.\n.PP\n\\fIM<monitor_name>\\fR\n.RS 4\nFocused monitor\\&.\n.RE\n.PP\n\\fIm<monitor_name>\\fR\n.RS 4\nUnfocused monitor\\&.\n.RE\n.PP\n\\fIO<desktop_name>\\fR\n.RS 4\nOccupied focused desktop\\&.\n.RE\n.PP\n\\fIo<desktop_name>\\fR\n.RS 4\nOccupied unfocused desktop\\&.\n.RE\n.PP\n\\fIF<desktop_name>\\fR\n.RS 4\nFree focused desktop\\&.\n.RE\n.PP\n\\fIf<desktop_name>\\fR\n.RS 4\nFree unfocused desktop\\&.\n.RE\n.PP\n\\fIU<desktop_name>\\fR\n.RS 4\nUrgent focused desktop\\&.\n.RE\n.PP\n\\fIu<desktop_name>\\fR\n.RS 4\nUrgent unfocused desktop\\&.\n.RE\n.PP\n\\fIL(T|M)\\fR\n.RS 4\nLayout of the focused desktop of a monitor\\&.\n.RE\n.PP\n\\fIT(T|P|F|=|@)\\fR\n.RS 4\nState of the focused node of a focused desktop\\&.\n.RE\n.PP\n\\fIG(S?P?L?M?)\\fR\n.RS 4\nActive flags of the focused node of a focused desktop\\&.\n.RE\n.SH \"ENVIRONMENT VARIABLES\"\n.PP\n\\fIBSPWM_SOCKET\\fR\n.RS 4\nThe path of the socket used for the communication between\n\\fBbspc\\fR\nand\n\\fBbspwm\\fR\\&. If it isn\\(cqt defined, then the following path is used:\n\\fI/tmp/bspwm<host_name>_<display_number>_<screen_number>\\-socket\\fR\\&.\n.RE\n.SH \"CONTRIBUTORS\"\n.sp\n.RS 4\n.ie n \\{\\\n\\h'-04'\\(bu\\h'+03'\\c\n.\\}\n.el \\{\\\n.sp -1\n.IP \\(bu 2.3\n.\\}\nSteven Allen <steven at stebalien\\&.com>\n.RE\n.sp\n.RS 4\n.ie n \\{\\\n\\h'-04'\\(bu\\h'+03'\\c\n.\\}\n.el \\{\\\n.sp -1\n.IP \\(bu 2.3\n.\\}\nThomas Adam <thomas at xteddy\\&.org>\n.RE\n.sp\n.RS 4\n.ie n \\{\\\n\\h'-04'\\(bu\\h'+03'\\c\n.\\}\n.el \\{\\\n.sp -1\n.IP \\(bu 2.3\n.\\}\nIvan Kanakarakis <ivan\\&.kanak at gmail\\&.com>\n.RE\n.SH \"AUTHOR\"\n.sp\nBastien Dejean <nihilhill at gmail\\&.com>\n"
  },
  {
    "path": "doc/bspwm.1.asciidoc",
    "content": ":man source:   Bspwm\n:man version:  {revnumber}\n:man manual:   Bspwm Manual\n\nbspwm(1)\n========\n\nName\n----\n\nbspwm - Binary space partitioning window manager\n\nSynopsis\n--------\n\n*bspwm* [*-h*|*-v*|*-c* 'CONFIG_PATH']\n\n*bspc --print-socket-path*\n\n*bspc* 'DOMAIN' ['SELECTOR'] 'COMMANDS'\n\n*bspc* 'COMMAND' ['OPTIONS'] ['ARGUMENTS']\n\nDescription\n-----------\n\n*bspwm* is a tiling window manager that represents windows as the leaves of a full binary tree.\n\nIt is controlled and configured via *bspc*.\n\n\nOptions\n-------\n\n*-h*::\n\tPrint the synopsis and exit.\n\n*-v*::\n\tPrint the version and exit.\n\n*-c* 'CONFIG_PATH'::\n\tUse the given configuration file.\n\n*--print-socket-path*::\n    Print the *bspwm* socket path and exit.\n\nCommon Definitions\n------------------\n\n----\nDIR         := north | west | south | east\nCYCLE_DIR   := next | prev\n----\n\nSelectors\n---------\n\nSelectors are used to select a target node, desktop, or monitor. A selector\ncan either describe the target relatively or name it globally.\n\nSelectors consist of an optional reference, a descriptor and any number of\nnon-conflicting modifiers as follows:\n\n\t[REFERENCE#]DESCRIPTOR(.MODIFIER)*\n\nThe relative targets are computed in relation to the given reference (the\ndefault reference value is *focused*).\n\nAn exclamation mark can be prepended to any modifier in order to reverse its\nmeaning.\n\nThe following characters cannot be used in monitor or desktop names: *#*, *:*, *.*.\n\nThe special selector *%<name>* can be used to select a monitor or a desktop with an invalid name.\n\nNode\n~~~~\n\nSelect a node.\n\n----\nNODE_SEL := [NODE_SEL#](DIR|CYCLE_DIR|PATH|any|first_ancestor|last|newest|\n                        older|newer|focused|pointed|biggest|smallest|\n                        <node_id>)[.[!]focused][.[!]active][.[!]automatic][.[!]local]\n                                  [.[!]leaf][.[!]window][.[!]STATE][.[!]FLAG][.[!]LAYER][.[!]SPLIT_TYPE]\n                                  [.[!]same_class][.[!]descendant_of][.[!]ancestor_of]\n\nSTATE := tiled|pseudo_tiled|floating|fullscreen\n\nFLAG := hidden|sticky|private|locked|marked|urgent\n\nLAYER := below|normal|above\n\nSPLIT_TYPE := horizontal|vertical\n\nPATH := @[DESKTOP_SEL:][[/]JUMP](/JUMP)*\n\nJUMP := first|1|second|2|brother|parent|DIR\n----\n\nDescriptors\n^^^^^^^^^^^\n\n'DIR'::\n\tSelects the window in the given (spacial) direction relative to the reference node.\n\n'CYCLE_DIR'::\n\tSelects the node in the given (cyclic) direction relative to the reference node within a depth-first in-order traversal of the tree.\n\n'PATH'::\n\tSelects the node at the given path.\n\nany::\n\tSelects the first node that matches the given selectors.\n\nfirst_ancestor::\n\tSelects the first ancestor of the reference node that matches the given selectors.\n\nlast::\n\tSelects the previously focused node relative to the reference node.\n\nnewest::\n\tSelects the newest node in the history of the focused node.\n\nolder::\n\tSelects the node older than the reference node in the history.\n\nnewer::\n\tSelects the node newer than the reference node in the history.\n\nfocused::\n\tSelects the currently focused node.\n\npointed::\n\tSelects the leaf under the pointer.\n\nbiggest::\n\tSelects the biggest leaf.\n\nsmallest::\n\tSelects the smallest leaf.\n\n<node_id>::\n\tSelects the node with the given ID.\n\nPath Jumps\n^^^^^^^^^^\n\nThe initial node is the focused node (or the root if the path starts with '/') of the reference desktop (or the selected desktop if the path has a 'DESKTOP_SEL' prefix).\n\n1|first::\n\tJumps to the first child.\n\n2|second::\n\tJumps to the second child.\n\nbrother::\n\tJumps to the brother node.\n\nparent::\n\tJumps to the parent node.\n\n'DIR'::\n\tJumps to the node holding the edge in the given direction.\n\nModifiers\n^^^^^^^^^\n\n[!]focused::\n\tOnly consider the focused node.\n\n[!]active::\n\tOnly consider nodes that are the focused node of their desktop.\n\n[!]automatic::\n\tOnly consider nodes in automatic insertion mode. See also *--presel-dir* under *Node* in the *DOMAINS* section below.\n\n[!]local::\n\tOnly consider nodes in the reference desktop.\n\n[!]leaf::\n\tOnly consider leaf nodes.\n\n[!]window::\n\tOnly consider nodes that hold a window.\n\n[!](tiled|pseudo_tiled|floating|fullscreen)::\n\tOnly consider windows in the given state.\n\n[!]same_class::\n\tOnly consider windows that have the same class as the reference window.\n\n[!]descendant_of::\n\tOnly consider nodes that are descendants of the reference node.\n\n[!]ancestor_of::\n\tOnly consider nodes that are ancestors of the reference node.\n\n[!](hidden|sticky|private|locked|marked|urgent)::\n\tOnly consider windows that have the given flag set.\n\n[!](below|normal|above)::\n\tOnly consider windows in the given layer.\n\n[!](horizontal|vertical)::\n\tOnly consider nodes with the given split type.\n\n\nDesktop\n~~~~~~~\n\nSelect a desktop.\n\n----\nDESKTOP_SEL := [DESKTOP_SEL#](CYCLE_DIR|any|last|newest|older|newer|\n                              [MONITOR_SEL:](focused|^<n>)|\n                              <desktop_id>|<desktop_name>)[.[!]focused][.[!]active]\n                                                          [.[!]occupied][.[!]urgent][.[!]local]\n                                                          [.[!]LAYOUT][.[!]user_LAYOUT]\n\nLAYOUT := tiled|monocle\n----\n\nDescriptors\n^^^^^^^^^^^\n\n'CYCLE_DIR'::\n\tSelects the desktop in the given direction relative to the reference desktop.\n\nany::\n\tSelects the first desktop that matches the given selectors.\n\nlast::\n\tSelects the previously focused desktop relative to the reference desktop.\n\nnewest::\n\tSelects the newest desktop in the history of the focused desktops.\n\nolder::\n\tSelects the desktop older than the reference desktop in the history.\n\nnewer::\n\tSelects the desktop newer than the reference desktop in the history.\n\nfocused::\n\tSelects the currently focused desktop.\n\n^<n>::\n\tSelects the nth desktop. If *MONITOR_SEL* is given, selects the nth desktop on the selected monitor.\n\n<desktop_id>::\n\tSelects the desktop with the given ID.\n\n<desktop_name>::\n\tSelects the desktop with the given name.\n\nModifiers\n^^^^^^^^^\n\n[!]focused::\n\tOnly consider the focused desktop.\n\n[!]active::\n\tOnly consider desktops that are the focused desktop of their monitor.\n\n[!]occupied::\n\tOnly consider occupied desktops.\n\n[!]urgent::\n\tOnly consider urgent desktops.\n\n[!]local::\n\tOnly consider desktops inside the reference monitor.\n\n[!](tiled|monocle)::\n\tOnly consider desktops with the given layout.\n\n[!](user_tiled|user_monocle)::\n\tOnly consider desktops which have the given layout as userLayout.\n\nMonitor\n~~~~~~~\n\nSelect a monitor.\n\n----\nMONITOR_SEL := [MONITOR_SEL#](DIR|CYCLE_DIR|any|last|newest|older|newer|\n                              focused|pointed|primary|^<n>|\n                              <monitor_id>|<monitor_name>)[.[!]focused][.[!]occupied]\n----\n\nDescriptors\n^^^^^^^^^^^\n\n'DIR'::\n\tSelects the monitor in the given (spacial) direction relative to the reference monitor.\n\n'CYCLE_DIR'::\n\tSelects the monitor in the given (cyclic) direction relative to the reference monitor.\n\nany::\n\tSelects the first monitor that matches the given selectors.\n\nlast::\n\tSelects the previously focused monitor relative to the reference monitor.\n\nnewest::\n\tSelects the newest monitor in the history of the focused monitors.\n\nolder::\n\tSelects the monitor older than the reference monitor in the history.\n\nnewer::\n\tSelects the monitor newer than the reference monitor in the history.\n\nfocused::\n\tSelects the currently focused monitor.\n\npointed::\n\tSelects the monitor under the pointer.\n\nprimary::\n\tSelects the primary monitor.\n\n^<n>::\n\tSelects the nth monitor.\n\n<monitor_id>::\n\tSelects the monitor with the given ID.\n\n<monitor_name>::\n\tSelects the monitor with the given name.\n\n\nModifiers\n^^^^^^^^^\n\n[!]focused::\n\tOnly consider the focused monitor.\n\n[!]occupied::\n\tOnly consider monitors where the focused desktop is occupied.\n\n\n\nWindow States\n-------------\n\ntiled::\n\tIts size and position are determined by the window tree.\n\npseudo_tiled::\n\tA tiled window that automatically shrinks but doesn't stretch beyond its floating size.\n\nfloating::\n\tCan be moved/resized freely. Although it doesn't use any tiling space, it is still part of the window tree.\n\nfullscreen::\n\tFills its monitor rectangle and has no borders.\n\n\nNode Flags\n----------\n\nhidden::\n\tIs hidden and doesn't occupy any tiling space.\n\nsticky::\n\tStays in the focused desktop of its monitor.\n\nprivate::\n\tTries to keep the same tiling position/size.\n\nlocked::\n\tIgnores the *node --close* message.\n\nmarked::\n\tIs marked (useful for deferred actions). A marked node becomes unmarked after being sent on a preselected node.\n\nurgent::\n\tHas its urgency hint set. This flag is set externally.\n\n\nStacking Layers\n--------------\n\nThere's three stacking layers: BELOW, NORMAL and ABOVE.\n\nIn each layer, the windows are orderered as follows: tiled & pseudo-tiled < floating < fullscreen.\n\nReceptacles\n-----------\n\nA leaf node that doesn't hold any window is called a receptacle. When a node is inserted on a receptacle in automatic mode, it will replace the receptacle. A receptacle can be inserted on a node, preselected and killed. Receptacles can therefore be used to build a tree whose leaves are receptacles. Using the appropriate rules, one can then send windows on the leaves of this tree. This feature is used in 'examples/receptacles' to store and recreate layouts.\n\n\nDomains\n-------\n\nNode\n~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nnode ['NODE_SEL'] 'COMMANDS'\n\nIf 'NODE_SEL' is omitted, *focused* is assumed.\n\nCommands\n^^^^^^^^\n*-f*, *--focus* ['NODE_SEL']::\n\tFocus the selected or given node.\n\n*-a*, *--activate* ['NODE_SEL']::\n\tActivate the selected or given node.\n\n*-d*, *--to-desktop* 'DESKTOP_SEL' [*--follow*]::\n\tSend the selected node to the given desktop. If *--follow* is passed, the focused node will stay focused.\n\n*-m*, *--to-monitor* 'MONITOR_SEL' [*--follow*]::\n\tSend the selected node to the given monitor. If *--follow* is passed, the focused node will stay focused.\n\n*-n*, *--to-node* 'NODE_SEL' [*--follow*]::\n\tSend the selected node on the given node. If *--follow* is passed, the focused node will stay focused.\n\n*-s*, *--swap* 'NODE_SEL' [*--follow*]::\n\tSwap the selected node with the given node. If *--follow* is passed, the focused node will stay focused.\n\n*-p*, *--presel-dir* \\[~]'DIR'|cancel::\n\tPreselect the splitting area of the selected node (or cancel the preselection). If *~* is prepended to 'DIR' and the current preselection direction matches 'DIR', then the argument is interpreted as *cancel*. A node with a preselected area is said to be in \"manual insertion mode\".\n\n*-o*, *--presel-ratio* 'RATIO'::\n\tSet the splitting ratio of the preselection area.\n\n*-v*, *--move* 'dx' 'dy'::\n\tMove the selected window by 'dx' pixels horizontally and 'dy' pixels vertically.\n\n*-z*, *--resize* top|left|bottom|right|top_left|top_right|bottom_right|bottom_left 'dx' 'dy'::\n\tResize the selected window by moving the given handle by 'dx' pixels horizontally and 'dy' pixels vertically.\n\n*-y*, *--type* 'CYCLE_DIR'|horizontal|vertical::\n\tSet or cycle the splitting type of the selected node.\n\n*-r*, *--ratio* 'RATIO'|(+|-)('PIXELS'|'FRACTION')::\n\tSet the splitting ratio of the selected node (0 < 'RATIO' < 1).\n\n*-R*, *--rotate* '90|270|180'::\n\tRotate the tree rooted at the selected node.\n\n*-F*, *--flip* 'horizontal|vertical'::\n\tFlip the tree rooted at selected node.\n\n*-E*, *--equalize*::\n\tReset the split ratios of the tree rooted at the selected node to their default value.\n\n*-B*, *--balance*::\n\tAdjust the split ratios of the tree rooted at the selected node so that all windows occupy the same area.\n\n*-C*, *--circulate* forward|backward::\n\tCirculate the windows of the tree rooted at the selected node.\n\n*-t*, *--state* \\~|\\[~]'STATE'::\n\tSet the state of the selected window. If *\\~* is present and the current state matches 'STATE', then the argument is interpreted as its last state. If the argument is just *~* with 'STATE' omitted, then the state of the selected window is set to its last state.\n\n*-g*, *--flag* hidden|sticky|private|locked|marked[=on|off]::\n\tSet or toggle the given flag for the selected node.\n\n*-l*, *--layer* below|normal|above::\n\tSet the stacking layer of the selected window.\n\n*-i*, *--insert-receptacle*::\n\tInsert a receptacle node at the selected node.\n\n*-c*, *--close*::\n\tClose the windows rooted at the selected node.\n\n*-k*, *--kill*::\n\tKill the windows rooted at the selected node.\n\nDesktop\n~~~~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\ndesktop ['DESKTOP_SEL'] 'COMMANDS'\n\nIf 'DESKTOP_SEL' is omitted, *focused* is assumed.\n\nCOMMANDS\n^^^^^^^^\n*-f*, *--focus* ['DESKTOP_SEL']::\n\tFocus the selected or given desktop.\n\n*-a*, *--activate* ['DESKTOP_SEL']::\n\tActivate the selected or given desktop.\n\n*-m*, *--to-monitor* 'MONITOR_SEL' [*--follow*]::\n\tSend the selected desktop to the given monitor. If *--follow* is passed, the focused desktop will stay focused.\n\n*-s*, *--swap* 'DESKTOP_SEL' [*--follow*]::\n\tSwap the selected desktop with the given desktop. If *--follow* is passed, the focused desktop will stay focused.\n\n*-l*, *--layout* 'CYCLE_DIR'|monocle|tiled::\n\tSet or cycle the layout of the selected desktop.\n\n*-n*, *--rename* <new_name>::\n\tRename the selected desktop.\n\n*-b*, *--bubble* 'CYCLE_DIR'::\n\tBubble the selected desktop in the given direction.\n\n*-r*, *--remove*::\n\tRemove the selected desktop.\n\nMonitor\n~~~~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nmonitor ['MONITOR_SEL'] 'COMMANDS'\n\nIf 'MONITOR_SEL' is omitted, *focused* is assumed.\n\nCommands\n^^^^^^^^\n*-f*, *--focus* ['MONITOR_SEL']::\n\tFocus the selected or given monitor.\n\n*-s*, *--swap* 'MONITOR_SEL'::\n\tSwap the selected monitor with the given monitor.\n\n*-a*, *--add-desktops* <name>...::\n\tCreate desktops with the given names in the selected monitor.\n\n*-o*, *--reorder-desktops* <name>...::\n\tReorder the desktops of the selected monitor to match the given order.\n\n*-d*, *--reset-desktops* <name>...::\n\tRename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops.\n\n*-g*, *--rectangle* WxH+X+Y::\n\tSet the rectangle of the selected monitor.\n\n*-n*, *--rename* <new_name>::\n\tRename the selected monitor.\n\n*-r*, *--remove*::\n\tRemove the selected monitor.\n\nQuery\n~~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nquery 'COMMANDS' ['OPTIONS']\n\nCommands\n^^^^^^^^\n\nThe optional selectors are references.\n\n*-N*, *--nodes* ['NODE_SEL']::\n\tList the IDs of the matching nodes.\n\n*-D*, *--desktops* ['DESKTOP_SEL']::\n\tList the IDs (or names) of the matching desktops.\n\n*-M*, *--monitors* ['MONITOR_SEL']::\n\tList the IDs (or names) of the matching monitors.\n\n*-T*, *--tree*::\n\tPrint a JSON representation of the matching item.\n\nOptions\n^^^^^^^\n\n*-m*,*--monitor* ['MONITOR_SEL'|'MONITOR_MODIFIERS']::\n*-d*,*--desktop* ['DESKTOP_SEL'|'DESKTOP_MODIFIERS']::\n*-n*,*--node* ['NODE_SEL'|'NODE_MODIFIERS']::\n\tConstrain matches to the selected monitors, desktops or nodes.\n\n*--names*::\n\tPrint names instead of IDs. Can only be used with '-M' and '-D'.\n\nWm\n~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nwm 'COMMANDS'\n\nCommands\n^^^^^^^^\n\n*-d*, *--dump-state*::\n\tDump the current world state on standard output.\n\n*-l*, *--load-state* <file_path>::\n\tLoad a world state from the given file. The path must be absolute.\n\n*-a*, *--add-monitor* <name> WxH+X+Y::\n\tAdd a monitor for the given name and rectangle.\n\n*-O*, *--reorder-monitors* <name>...::\n\tReorder the list of monitors to match the given order.\n\n*-o*, *--adopt-orphans*::\n\tManage all the unmanaged windows remaining from a previous session.\n\n*-h*, *--record-history* on|off::\n\tEnable or disable the recording of node focus history.\n\n*-g*, *--get-status*::\n\tPrint the current status information.\n\n*-r*, *--restart*::\n\tRestart the window manager\n\nRule\n~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nrule 'COMMANDS'\n\nCommands\n^^^^^^^^\n\n*-a*, *--add* (<class_name>|\\*)[:(<instance_name>|\\*)[:(<name>|\\*)]] [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL] [state=STATE] [layer=LAYER] [honor_size_hints=(true|false|tiled|floating)] [split_dir=DIR] [split_ratio=RATIO] [(hidden|sticky|private|locked|marked|center|follow|manage|focus|border)=(on|off)] [rectangle=WxH+X+Y]::\n\tCreate a new rule. Colons in the 'instance_name', 'class_name', or 'name'\n\tfields can be escaped with a backslash.\n\n*-r*, *--remove* ^<n>|head|tail|(<class_name>|\\*)[:(<instance_name>|\\*)[:(<name>|*)]]...::\n\tRemove the given rules.\n\n*-l*, *--list*::\n\tList the rules.\n\nConfig\n~~~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nconfig [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-n 'NODE_SEL'] <setting> [<value>]::\n\tGet or set the value of <setting>.\n\nSubscribe\n~~~~~~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\nsubscribe ['OPTIONS'] (all|report|monitor|desktop|node|...)*::\n\tContinuously print events. See the *EVENTS* section for the description of each event.\n\nOptions\n^^^^^^^\n\n*-f*, *--fifo*::\n\tPrint a path to a FIFO from which events can be read and return.\n\n*-c*, *--count* 'COUNT'::\n\tStop the corresponding *bspc* process after having received 'COUNT' events.\n\nQuit\n~~~~\n\nGeneral Syntax\n^^^^^^^^^^^^^^\n\nquit [<status>]::\n\tQuit with an optional exit status.\n\nExit Codes\n----------\n\nIf the server can't handle a message, *bspc* will return with a non-zero exit code.\n\nSettings\n--------\nColors are in the form '#RRGGBB', booleans are 'true', 'on', 'false' or 'off'.\n\nAll the boolean settings are 'false' by default unless stated otherwise.\n\nGlobal Settings\n~~~~~~~~~~~~~~~\n\n'normal_border_color'::\n\tColor of the border of an unfocused window.\n\n'active_border_color'::\n\tColor of the border of a focused window of an unfocused monitor.\n\n'focused_border_color'::\n\tColor of the border of a focused window of a focused monitor.\n\n'presel_feedback_color'::\n\tColor of the *node --presel-{dir,ratio}* message feedback area.\n\n'split_ratio'::\n\tDefault split ratio.\n\n'status_prefix'::\n\tPrefix prepended to each of the status lines.\n\n'external_rules_command'::\n\tAbsolute path to the command used to retrieve rule consequences. The command will receive the following arguments: window ID, class name, instance name, and intermediate consequences. The output of that command must have the following format: *key1=value1 key2=value2 ...* (the valid key/value pairs are given in the description of the 'rule' command).\n\n'automatic_scheme'::\n\tThe insertion scheme used when the insertion point is in automatic mode. Accept the following values: *longest_side*, *alternate*, *spiral*.\n\n'initial_polarity'::\n\tOn which child should a new window be attached when adding a window on a single window tree in automatic mode. Accept the following values: *first_child*, *second_child*.\n\n'directional_focus_tightness'::\n\tThe tightness of the algorithm used to decide whether a window is on the 'DIR' side of another window. Accept the following values: *high*, *low*.\n\n'removal_adjustment'::\n\tAdjust the brother when unlinking a node from the tree in accordance with the automatic insertion scheme.\n\n'presel_feedback'::\n\tDraw the preselection feedback area. Defaults to 'true'.\n\n'borderless_monocle'::\n\tRemove borders of tiled windows for the *monocle* desktop layout.\n\n'gapless_monocle'::\n\tRemove gaps of tiled windows for the *monocle* desktop layout.\n\n'top_monocle_padding'::\n'right_monocle_padding'::\n'bottom_monocle_padding'::\n'left_monocle_padding'::\n\tPadding space added at the sides of the screen for the *monocle* desktop layout.\n\n'single_monocle'::\n\tSet the desktop layout to *monocle* if there's only one tiled window in the tree.\n\n'borderless_singleton'::\n\tRemove borders of the only window on the only monitor regardless its layout.\n\n'pointer_motion_interval'::\n\tThe minimum interval, in milliseconds, between two motion notify events.\n\n'pointer_modifier'::\n\tKeyboard modifier used for moving or resizing windows. Accept the following values: *shift*, *control*, *lock*, *mod1*, *mod2*, *mod3*, *mod4*, *mod5*.\n\n'pointer_action1'::\n'pointer_action2'::\n'pointer_action3'::\n\tAction performed when pressing 'pointer_modifier' + 'button<n>'. Accept the following values: *move*, *resize_side*, *resize_corner*, *focus*, *none*.\n\n'click_to_focus'::\n\tButton used for focusing a window (or a monitor). The possible values are: *button1*, *button2*, *button3*, *any*, *none*. Defaults to *button1*.\n\n'swallow_first_click'::\n\tDon't replay the click that makes a window focused if 'click_to_focus' isn't *none*.\n\n'focus_follows_pointer'::\n\tFocus the window under the pointer.\n\n'pointer_follows_focus'::\n\tWhen focusing a window, put the pointer at its center.\n\n'pointer_follows_monitor'::\n\tWhen focusing a monitor, put the pointer at its center.\n\n'mapping_events_count'::\n\tHandle the next *mapping_events_count* mapping notify events. A negative value implies that every event needs to be handled.\n\n'ignore_ewmh_focus'::\n\tIgnore EWMH focus requests coming from applications.\n\n'ignore_ewmh_fullscreen'::\n\tBlock the fullscreen state transitions that originate from an EWMH request. The possible values are: *none*, *all*, or a comma separated list of the following values: *enter*, *exit*.\n\n'ignore_ewmh_struts'::\n\tIgnore strut hinting from clients requesting to reserve space (i.e. task bars).\n\n'center_pseudo_tiled'::\n\tCenter pseudo tiled windows into their tiling rectangles. Defaults to 'true'.\n\n'remove_disabled_monitors'::\n\tConsider disabled monitors as disconnected.\n\n'remove_unplugged_monitors'::\n\tRemove unplugged monitors.\n\n'merge_overlapping_monitors'::\n\tMerge overlapping monitors (the bigger remains).\n\nMonitor and Desktop Settings\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n'top_padding'::\n'right_padding'::\n'bottom_padding'::\n'left_padding'::\n\tPadding space added at the sides of the monitor or desktop.\n\nDesktop Settings\n~~~~~~~~~~~~~~~~\n\n'window_gap'::\n\tSize of the gap that separates windows.\n\nNode Settings\n~~~~~~~~~~~~~\n\n'border_width'::\n\tWindow border width.\n\n'honor_size_hints'::\n\tIf 'true', apply ICCCM window size hints to all windows. If 'floating', only apply them to floating and pseudo tiled windows. If 'tiled', only apply them to tiled windows. If 'false', don't apply them. Defaults to 'false'.\n\nPointer Bindings\n----------------\n\n'click_to_focus'::\n\tFocus the window (or the monitor) under the pointer if the value isn't *none*.\n\n'pointer_modifier' + 'button1'::\n\tMove the window under the pointer.\n\n'pointer_modifier' + 'button2'::\n\tResize the window under the pointer by dragging the nearest side.\n\n'pointer_modifier' + 'button3'::\n\tResize the window under the pointer by dragging the nearest corner.\n\nThe behavior of 'pointer_modifier' + 'button<n>' can be modified through the 'pointer_action<n>' setting.\n\nEvents\n------\n\n'report'::\n\tSee the next section for the description of the format.\n\n'monitor_add <monitor_id> <monitor_name> <monitor_geometry>'::\n\tA monitor is added.\n\n'monitor_rename <monitor_id> <old_name> <new_name>'::\n\tA monitor is renamed.\n\n'monitor_remove <monitor_id>'::\n\tA monitor is removed.\n\n'monitor_swap <src_monitor_id> <dst_monitor_id>'::\n\tA monitor is swapped.\n\n'monitor_focus <monitor_id>'::\n\tA monitor is focused.\n\n'monitor_geometry <monitor_id> <monitor_geometry>'::\n\tThe geometry of a monitor changed.\n\n'desktop_add <monitor_id> <desktop_id> <desktop_name>'::\n\tA desktop is added.\n\n'desktop_rename <monitor_id> <desktop_id> <old_name> <new_name>'::\n\tA desktop is renamed.\n\n'desktop_remove <monitor_id> <desktop_id>'::\n\tA desktop is removed.\n\n'desktop_swap <src_monitor_id> <src_desktop_id> <dst_monitor_id> <dst_desktop_id>'::\n\tA desktop is swapped.\n\n'desktop_transfer <src_monitor_id> <src_desktop_id> <dst_monitor_id>'::\n\tA desktop is transferred.\n\n'desktop_focus <monitor_id> <desktop_id>'::\n\tA desktop is focused.\n\n'desktop_activate <monitor_id> <desktop_id>'::\n\tA desktop is activated.\n\n'desktop_layout <monitor_id> <desktop_id> tiled|monocle'::\n\tThe layout of a desktop changed.\n\n'node_add <monitor_id> <desktop_id> <ip_id> <node_id>'::\n\tA node is added.\n\n'node_remove <monitor_id> <desktop_id> <node_id>'::\n\tA node is removed.\n\n'node_swap <src_monitor_id> <src_desktop_id> <src_node_id> <dst_monitor_id> <dst_desktop_id> <dst_node_id>'::\n\tA node is swapped.\n\n'node_transfer <src_monitor_id> <src_desktop_id> <src_node_id> <dst_monitor_id> <dst_desktop_id> <dst_node_id>'::\n\tA node is transferred.\n\n'node_focus <monitor_id> <desktop_id> <node_id>'::\n\tA node is focused.\n\n'node_activate <monitor_id> <desktop_id> <node_id>'::\n\tA node is activated.\n\n'node_presel <monitor_id> <desktop_id> <node_id> (dir DIR|ratio RATIO|cancel)'::\n\tA node is preselected.\n\n'node_stack <node_id_1> below|above <node_id_2>'::\n\tA node is stacked below or above another node.\n\n'node_geometry <monitor_id> <desktop_id> <node_id> <node_geometry>'::\n\tThe geometry of a window changed.\n\n'node_state <monitor_id> <desktop_id> <node_id> tiled|pseudo_tiled|floating|fullscreen on|off'::\n\tThe state of a window changed.\n\n'node_flag <monitor_id> <desktop_id> <node_id> hidden|sticky|private|locked|marked|urgent on|off'::\n\tOne of the flags of a node changed.\n\n'node_layer <monitor_id> <desktop_id> <node_id> below|normal|above'::\n\tThe layer of a window changed.\n\n'pointer_action <monitor_id> <desktop_id> <node_id> move|resize_corner|resize_side begin|end'::\n\tA pointer action occurred.\n\nPlease note that *bspwm* initializes monitors before it reads messages on its socket, therefore the initial monitor events can't be received.\n\nReport Format\n-------------\n\nEach report event message is composed of items separated by colons.\n\nEach item has the form '<type><value>' where '<type>' is the first character of the item.\n\n'M<monitor_name>'::\n\tFocused monitor.\n\n'm<monitor_name>'::\n\tUnfocused monitor.\n\n'O<desktop_name>'::\n\tOccupied focused desktop.\n\n'o<desktop_name>'::\n\tOccupied unfocused desktop.\n\n'F<desktop_name>'::\n\tFree focused desktop.\n\n'f<desktop_name>'::\n\tFree unfocused desktop.\n\n'U<desktop_name>'::\n\tUrgent focused desktop.\n\n'u<desktop_name>'::\n\tUrgent unfocused desktop.\n\n'L(T|M)'::\n\tLayout of the focused desktop of a monitor.\n\n'T(T|P|F|=|@)'::\n\tState of the focused node of a focused desktop.\n\n'G(S?P?L?M?)'::\n\tActive flags of the focused node of a focused desktop.\n\nEnvironment Variables\n---------------------\n\n'BSPWM_SOCKET'::\n\tThe path of the socket used for the communication between *bspc* and *bspwm*. If it isn't defined, then the following path is used: '/tmp/bspwm<host_name>_<display_number>_<screen_number>-socket'.\n\nContributors\n------------\n\n* Steven Allen <steven at stebalien.com>\n* Thomas Adam <thomas at xteddy.org>\n* Ivan Kanakarakis <ivan.kanak at gmail.com>\n\nAuthor\n------\n\nBastien Dejean <nihilhill at gmail.com>\n"
  },
  {
    "path": "examples/bspwmrc",
    "content": "#! /bin/sh\n\npgrep -x sxhkd > /dev/null || sxhkd &\n\nbspc monitor -d I II III IV V VI VII VIII IX X\n\nbspc config border_width         2\nbspc config window_gap          12\n\nbspc config split_ratio          0.52\nbspc config borderless_monocle   true\nbspc config gapless_monocle      true\n\nbspc rule -a Gimp desktop='^8' state=floating follow=on\nbspc rule -a Chromium desktop='^2'\nbspc rule -a mplayer2 state=floating\nbspc rule -a Kupfer.py focus=on\nbspc rule -a Screenkey manage=off\n"
  },
  {
    "path": "examples/external_rules/bspwmrc",
    "content": "#! /bin/sh\n\nbspc config external_rules_command \"$(which external_rules)\"\n"
  },
  {
    "path": "examples/external_rules/external_rules",
    "content": "#! /bin/sh\n\nwid=$1\nclass=$2\ninstance=$3\nconsequences=$4\n\nif [ \"$instance\" = fontforge ] ; then\n\ttitle=$(xtitle \"$wid\")\n\tcase \"$title\" in\n\t\tLayers|Tools|Warning)\n\t\t\techo \"focus=off\"\n\t\t\t;;\n\tesac\nfi\n\ncase \"$class\" in\n\tLutris|Liferea)\n\t\teval \"$consequences\"\n\t\t[ \"$state\" ] || echo \"state=pseudo_tiled\"\n\t\t;;\nesac\n"
  },
  {
    "path": "examples/overlapping_borders/bspwmrc",
    "content": "#! /bin/sh\n\nBW=3\nbspc config border_width $BW\nbspc config window_gap -$BW\nfor side in top right bottom left ; do\n\tbspc config ${side}_padding $BW\ndone\n"
  },
  {
    "path": "examples/panel/bspwmrc",
    "content": "#! /bin/sh\n\npgrep -x panel > /dev/null || panel &\n"
  },
  {
    "path": "examples/panel/panel",
    "content": "#! /bin/sh\n\nif xdo id -a \"$PANEL_WM_NAME\" > /dev/null ; then\n\tprintf \"%s\\n\" \"The panel is already running.\" >&2\n\texit 1\nfi\n\ntrap 'trap - TERM; kill 0' INT TERM QUIT EXIT\n\n[ -e \"$PANEL_FIFO\" ] && rm \"$PANEL_FIFO\"\nmkfifo \"$PANEL_FIFO\"\n\nxtitle -sf 'T%s\\n' > \"$PANEL_FIFO\" &\nclock -sf 'S%a %H:%M' > \"$PANEL_FIFO\" &\nbspc subscribe report > \"$PANEL_FIFO\" &\n\n. panel_colors\n\npanel_bar < \"$PANEL_FIFO\" | lemonbar -a 32 -u 2 -n \"$PANEL_WM_NAME\" -g x$PANEL_HEIGHT -f \"$PANEL_FONT\" -F \"$COLOR_DEFAULT_FG\" -B \"$COLOR_DEFAULT_BG\" | sh &\n\nwid=$(xdo id -m -a \"$PANEL_WM_NAME\")\nxdo above -t \"$(xdo id -N Bspwm -n root | sort | head -n 1)\" \"$wid\"\n\nwait\n"
  },
  {
    "path": "examples/panel/panel_bar",
    "content": "#! /bin/sh\n#\n# Example panel for lemonbar\n\n. panel_colors\n\nnum_mon=$(bspc query -M | wc -l)\n\nwhile read -r line ; do\n\tcase $line in\n\t\tS*)\n\t\t\t# clock output\n\t\t\tsys=\"%{F$COLOR_SYS_FG}%{B$COLOR_SYS_BG} ${line#?} %{B-}%{F-}\"\n\t\t\t;;\n\t\tT*)\n\t\t\t# xtitle output\n\t\t\ttitle=\"%{F$COLOR_TITLE_FG}%{B$COLOR_TITLE_BG} ${line#?} %{B-}%{F-}\"\n\t\t\t;;\n\t\tW*)\n\t\t\t# bspwm's state\n\t\t\twm=\n\t\t\tIFS=':'\n\t\t\tset -- ${line#?}\n\t\t\twhile [ $# -gt 0 ] ; do\n\t\t\t\titem=$1\n\t\t\t\tname=${item#?}\n\t\t\t\tcase $item in\n\t\t\t\t\t[mM]*)\n\t\t\t\t\t\tcase $item in\n\t\t\t\t\t\t\tm*)\n\t\t\t\t\t\t\t\t# monitor\n\t\t\t\t\t\t\t\tFG=$COLOR_MONITOR_FG\n\t\t\t\t\t\t\t\tBG=$COLOR_MONITOR_BG\n\t\t\t\t\t\t\t\ton_focused_monitor=\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\tM*)\n\t\t\t\t\t\t\t\t# focused monitor\n\t\t\t\t\t\t\t\tFG=$COLOR_FOCUSED_MONITOR_FG\n\t\t\t\t\t\t\t\tBG=$COLOR_FOCUSED_MONITOR_BG\n\t\t\t\t\t\t\t\ton_focused_monitor=1\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\tesac\n\t\t\t\t\t\t[ $num_mon -lt 2 ] && shift && continue\n\t\t\t\t\t\twm=\"${wm}%{F${FG}}%{B${BG}}%{A:bspc monitor -f ${name}:} ${name} %{A}%{B-}%{F-}\"\n\t\t\t\t\t\t;;\n\t\t\t\t\t[fFoOuU]*)\n\t\t\t\t\t\tcase $item in\n\t\t\t\t\t\t\tf*)\n\t\t\t\t\t\t\t\t# free desktop\n\t\t\t\t\t\t\t\tFG=$COLOR_FREE_FG\n\t\t\t\t\t\t\t\tBG=$COLOR_FREE_BG\n\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\tF*)\n\t\t\t\t\t\t\t\tif [ \"$on_focused_monitor\" ] ; then\n\t\t\t\t\t\t\t\t\t# focused free desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_FOCUSED_FREE_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_FOCUSED_FREE_BG\n\t\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t# active free desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_FREE_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_FREE_BG\n\t\t\t\t\t\t\t\t\tUL=$COLOR_FOCUSED_FREE_BG\n\t\t\t\t\t\t\t\tfi\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\to*)\n\t\t\t\t\t\t\t\t# occupied desktop\n\t\t\t\t\t\t\t\tFG=$COLOR_OCCUPIED_FG\n\t\t\t\t\t\t\t\tBG=$COLOR_OCCUPIED_BG\n\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\tO*)\n\t\t\t\t\t\t\t\tif [ \"$on_focused_monitor\" ] ; then\n\t\t\t\t\t\t\t\t\t# focused occupied desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_FOCUSED_OCCUPIED_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_FOCUSED_OCCUPIED_BG\n\t\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t# active occupied desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_OCCUPIED_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_OCCUPIED_BG\n\t\t\t\t\t\t\t\t\tUL=$COLOR_FOCUSED_OCCUPIED_BG\n\t\t\t\t\t\t\t\tfi\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\tu*)\n\t\t\t\t\t\t\t\t# urgent desktop\n\t\t\t\t\t\t\t\tFG=$COLOR_URGENT_FG\n\t\t\t\t\t\t\t\tBG=$COLOR_URGENT_BG\n\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\t\tU*)\n\t\t\t\t\t\t\t\tif [ \"$on_focused_monitor\" ] ; then\n\t\t\t\t\t\t\t\t\t# focused urgent desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_FOCUSED_URGENT_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_FOCUSED_URGENT_BG\n\t\t\t\t\t\t\t\t\tUL=$BG\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t# active urgent desktop\n\t\t\t\t\t\t\t\t\tFG=$COLOR_URGENT_FG\n\t\t\t\t\t\t\t\t\tBG=$COLOR_URGENT_BG\n\t\t\t\t\t\t\t\t\tUL=$COLOR_FOCUSED_URGENT_BG\n\t\t\t\t\t\t\t\tfi\n\t\t\t\t\t\t\t\t;;\n\t\t\t\t\t\tesac\n\t\t\t\t\t\twm=\"${wm}%{F${FG}}%{B${BG}}%{U${UL}}%{+u}%{A:bspc desktop -f ${name}:} ${name} %{A}%{B-}%{F-}%{-u}\"\n\t\t\t\t\t\t;;\n\t\t\t\t\t[LTG]*)\n\t\t\t\t\t\t# layout, state and flags\n\t\t\t\t\t\twm=\"${wm}%{F$COLOR_STATE_FG}%{B$COLOR_STATE_BG} ${name} %{B-}%{F-}\"\n\t\t\t\t\t\t;;\n\t\t\t\tesac\n\t\t\t\tshift\n\t\t\tdone\n\t\t\t;;\n\tesac\n\tprintf \"%s\\n\" \"%{l}${wm}%{c}${title}%{r}${sys}\"\ndone\n"
  },
  {
    "path": "examples/panel/panel_colors",
    "content": "COLOR_DEFAULT_FG=\"#a7a5a5\"\nCOLOR_DEFAULT_BG=\"#333232\"\nCOLOR_MONITOR_FG=\"#8dbcdf\"\nCOLOR_MONITOR_BG=\"#333232\"\nCOLOR_FOCUSED_MONITOR_FG=\"#b1d0e8\"\nCOLOR_FOCUSED_MONITOR_BG=\"#144b6c\"\nCOLOR_FREE_FG=\"#737171\"\nCOLOR_FREE_BG=\"#333232\"\nCOLOR_FOCUSED_FREE_FG=\"#000000\"\nCOLOR_FOCUSED_FREE_BG=\"#504e4e\"\nCOLOR_OCCUPIED_FG=\"#a7a5a5\"\nCOLOR_OCCUPIED_BG=\"#333232\"\nCOLOR_FOCUSED_OCCUPIED_FG=\"#d6d3d2\"\nCOLOR_FOCUSED_OCCUPIED_BG=\"#504e4e\"\nCOLOR_URGENT_FG=\"#f15d66\"\nCOLOR_URGENT_BG=\"#333232\"\nCOLOR_FOCUSED_URGENT_FG=\"#501d1f\"\nCOLOR_FOCUSED_URGENT_BG=\"#d5443e\"\nCOLOR_STATE_FG=\"#89b09c\"\nCOLOR_STATE_BG=\"#333232\"\nCOLOR_TITLE_FG=\"#a8a2c0\"\nCOLOR_TITLE_BG=\"#333232\"\nCOLOR_SYS_FG=\"#b1a57d\"\nCOLOR_SYS_BG=\"#333232\"\n"
  },
  {
    "path": "examples/panel/profile",
    "content": "PANEL_FIFO=/tmp/panel-fifo\nPANEL_HEIGHT=24\nPANEL_FONT=\"-*-fixed-*-*-*-*-10-*-*-*-*-*-*-*\"\nPANEL_WM_NAME=bspwm_panel\nexport PANEL_FIFO PANEL_HEIGHT PANEL_FONT PANEL_WM_NAME\n"
  },
  {
    "path": "examples/panel/sxhkdrc",
    "content": "super + alt + Escape\n\tpkill -x panel; bspc quit\n"
  },
  {
    "path": "examples/receptacles/README.md",
    "content": "The scripts present in this directory can be used to store and recreate layouts.\n\nBoth scripts take a JSON state (output of `bspc wm -d`) as input or argument.\n\n- `extract_canvas [state.json]` outputs a new JSON state where each leaf window is replaced by a receptacle.\n\n- `induce_rules [state.json]` outputs a list of commands that, if executed, will create rules to place each window in the corresponding receptacle.\n"
  },
  {
    "path": "examples/receptacles/extract_canvas",
    "content": "#! /usr/bin/env python3\n\nimport sys\nimport json\n\nsource = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin\nstate = json.load(source)\n\ndef nullify_clients(node):\n    if node is None:\n        return\n    elif node['client'] is None:\n        nullify_clients(node['firstChild'])\n        nullify_clients(node['secondChild'])\n    else:\n        node['client'] = None\n\nstate['clientsCount'] = 0\nstate['focusHistory'] = []\nstate['stackingList'] = []\n\nfor monitor in state['monitors']:\n    for desktop in monitor['desktops']:\n        desktop['focusedNodeId'] = 0\n        nullify_clients(desktop['root'])\n\nprint(json.dumps(state))\n"
  },
  {
    "path": "examples/receptacles/induce_rules",
    "content": "#! /usr/bin/env python3\n\nimport sys\nimport json\n\nsource = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin\nstate = json.load(source)\n\ndef print_rules(prefix, node, path):\n    if node is None:\n        return\n    elif node['client'] is None:\n        print_rules(prefix, node['firstChild'], path+['1'])\n        print_rules(prefix, node['secondChild'], path+['2'])\n    else:\n        client = node['client']\n        print('bspc rule -a {}:{} -o node={}{}'.format(client['className'],\n                                                       client['instanceName'],\n                                                       prefix, '/'.join(path)))\n\nfor i, monitor in enumerate(state['monitors']):\n    for j, desktop in enumerate(monitor['desktops']):\n        print_rules('@^{}:^{}:/'.format(i+1, j+1), desktop['root'], [])\n"
  },
  {
    "path": "examples/sxhkdrc",
    "content": "#\n# wm independent hotkeys\n#\n\n# terminal emulator\nsuper + Return\n\turxvt\n\n# program launcher\nsuper + @space\n\tdmenu_run\n\n# make sxhkd reload its configuration files:\nsuper + Escape\n\tpkill -USR1 -x sxhkd\n\n#\n# bspwm hotkeys\n#\n\n# quit/restart bspwm\nsuper + alt + {q,r}\n\tbspc {quit,wm -r}\n\n# close and kill\nsuper + {_,shift + }w\n\tbspc node -{c,k}\n\n# alternate between the tiled and monocle layout\nsuper + m\n\tbspc desktop -l next\n\n# send the newest marked node to the newest preselected node\nsuper + y\n\tbspc node newest.marked.local -n newest.!automatic.local\n\n# swap the current node and the biggest window\nsuper + g\n\tbspc node -s biggest.window\n\n#\n# state/flags\n#\n\n# set the window state\nsuper + {t,shift + t,s,f}\n\tbspc node -t {tiled,pseudo_tiled,floating,fullscreen}\n\n# set the node flags\nsuper + ctrl + {m,x,y,z}\n\tbspc node -g {marked,locked,sticky,private}\n\n#\n# focus/swap\n#\n\n# focus the node in the given direction\nsuper + {_,shift + }{h,j,k,l}\n\tbspc node -{f,s} {west,south,north,east}\n\n# focus the node for the given path jump\nsuper + {p,b,comma,period}\n\tbspc node -f @{parent,brother,first,second}\n\n# focus the next/previous window in the current desktop\nsuper + {_,shift + }c\n\tbspc node -f {next,prev}.local.!hidden.window\n\n# focus the next/previous desktop in the current monitor\nsuper + bracket{left,right}\n\tbspc desktop -f {prev,next}.local\n\n# focus the last node/desktop\nsuper + {grave,Tab}\n\tbspc {node,desktop} -f last\n\n# focus the older or newer node in the focus history\nsuper + {o,i}\n\tbspc wm -h off; \\\n\tbspc node {older,newer} -f; \\\n\tbspc wm -h on\n\n# focus or send to the given desktop\nsuper + {_,shift + }{1-9,0}\n\tbspc {desktop -f,node -d} '^{1-9,10}'\n\n#\n# preselect\n#\n\n# preselect the direction\nsuper + ctrl + {h,j,k,l}\n\tbspc node -p {west,south,north,east}\n\n# preselect the ratio\nsuper + ctrl + {1-9}\n\tbspc node -o 0.{1-9}\n\n# cancel the preselection for the focused node\nsuper + ctrl + space\n\tbspc node -p cancel\n\n# cancel the preselection for the focused desktop\nsuper + ctrl + shift + space\n\tbspc query -N -d | xargs -I id -n 1 bspc node id -p cancel\n\n#\n# move/resize\n#\n\n# expand a window by moving one of its side outward\nsuper + alt + {h,j,k,l}\n\tbspc node -z {left -20 0,bottom 0 20,top 0 -20,right 20 0}\n\n# contract a window by moving one of its side inward\nsuper + alt + shift + {h,j,k,l}\n\tbspc node -z {right -20 0,top 0 20,bottom 0 -20,left 20 0}\n\n# move a floating window\nsuper + {Left,Down,Up,Right}\n\tbspc node -v {-20 0,0 20,0 -20,20 0}\n"
  },
  {
    "path": "src/bspc.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <poll.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include \"helpers.h\"\n#include \"common.h\"\n\nint main(int argc, char *argv[])\n{\n\tint sock_fd;\n\tstruct sockaddr_un sock_address;\n\tchar msg[BUFSIZ], rsp[BUFSIZ];\n\n\tif (argc < 2) {\n\t\terr(\"No arguments given.\\n\");\n\t}\n\n\tsock_address.sun_family = AF_UNIX;\n\tchar *sp;\n\n\tsp = getenv(SOCKET_ENV_VAR);\n\tif (sp != NULL) {\n\t\tsnprintf(sock_address.sun_path, sizeof(sock_address.sun_path), \"%s\", sp);\n\t} else {\n\t\tchar *host = NULL;\n\t\tint dn = 0, sn = 0;\n\t\tif (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {\n\t\t\tsnprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);\n\t\t}\n\t\tfree(host);\n\t}\n\n\tif (streq(argv[1], \"--print-socket-path\")) {\n\t\tprintf(\"%s\\n\", sock_address.sun_path);\n\t\treturn EXIT_SUCCESS;\n\t}\n\n\tif ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {\n\t\terr(\"Failed to create the socket.\\n\");\n\t}\n\n\tif (connect(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {\n\t\terr(\"Failed to connect to the socket.\\n\");\n\t}\n\n\targc--, argv++;\n\tint msg_len = 0;\n\n\tfor (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {\n\t\tn = snprintf(msg + offset, rem, \"%s%c\", *argv, 0);\n\t\tmsg_len += n;\n\t}\n\n\tif (send(sock_fd, msg, msg_len, 0) == -1) {\n\t\terr(\"Failed to send the data.\\n\");\n\t}\n\n\tint ret = EXIT_SUCCESS, nb;\n\n\tstruct pollfd fds[] = {\n\t\t{sock_fd, POLLIN, 0},\n\t\t{STDOUT_FILENO, POLLHUP, 0},\n\t};\n\n\twhile (poll(fds, 2, -1) > 0) {\n\t\tif (fds[0].revents & POLLIN) {\n\t\t\tif ((nb = recv(sock_fd, rsp, sizeof(rsp)-1, 0)) > 0) {\n\t\t\t\trsp[nb] = '\\0';\n\t\t\t\tif (rsp[0] == FAILURE_MESSAGE[0]) {\n\t\t\t\t\tret = EXIT_FAILURE;\n\t\t\t\t\tfprintf(stderr, \"%s\", rsp + 1);\n\t\t\t\t\tfflush(stderr);\n\t\t\t\t} else {\n\t\t\t\t\tfprintf(stdout, \"%s\", rsp);\n\t\t\t\t\tfflush(stdout);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (fds[1].revents & (POLLERR | POLLHUP)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tclose(sock_fd);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/bspwm.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <unistd.h>\n#include <stdbool.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <xcb/xinerama.h>\n#include <xcb/xcb_aux.h>\n#include \"types.h\"\n#include \"desktop.h\"\n#include \"monitor.h\"\n#include \"settings.h\"\n#include \"messages.h\"\n#include \"pointer.h\"\n#include \"events.h\"\n#include \"common.h\"\n#include \"window.h\"\n#include \"history.h\"\n#include \"ewmh.h\"\n#include \"rule.h\"\n#include \"restore.h\"\n#include \"query.h\"\n#include \"bspwm.h\"\n\nxcb_connection_t *dpy;\nint default_screen, screen_width, screen_height;\nuint32_t clients_count;\nxcb_screen_t *screen;\nxcb_window_t root;\nchar config_path[MAXLEN];\n\nmonitor_t *mon;\nmonitor_t *mon_head;\nmonitor_t *mon_tail;\nmonitor_t *pri_mon;\nhistory_t *history_head;\nhistory_t *history_tail;\nhistory_t *history_needle;\nrule_t *rule_head;\nrule_t *rule_tail;\nstacking_list_t *stack_head;\nstacking_list_t *stack_tail;\nsubscriber_list_t *subscribe_head;\nsubscriber_list_t *subscribe_tail;\npending_rule_t *pending_rule_head;\npending_rule_t *pending_rule_tail;\n\nxcb_window_t meta_window;\nmotion_recorder_t motion_recorder;\nxcb_atom_t WM_STATE;\nxcb_atom_t WM_TAKE_FOCUS;\nxcb_atom_t WM_DELETE_WINDOW;\nint exit_status;\n\nbool auto_raise;\nbool sticky_still;\nbool hide_sticky;\nbool record_history;\nvolatile sig_atomic_t running;\nbool restart;\nbool randr;\n\nint main(int argc, char *argv[])\n{\n\tfd_set descriptors;\n\tchar socket_path[MAXLEN];\n\tchar state_path[MAXLEN] = {0};\n\tint run_level = 0;\n\tconfig_path[0] = '\\0';\n\tint sock_fd = -1, cli_fd, dpy_fd, max_fd, n;\n\tstruct sockaddr_un sock_address;\n\tchar msg[BUFSIZ] = {0};\n\txcb_generic_event_t *event;\n\tchar *end;\n\tint opt;\n\tstruct sigaction sigact;\n\n\twhile ((opt = getopt(argc, argv, \"hvc:s:o:\")) != -1) {\n\t\tswitch (opt) {\n\t\t\tcase 'h':\n\t\t\t\tprintf(WM_NAME \" [-h|-v|-c CONFIG_PATH]\\n\");\n\t\t\t\texit(EXIT_SUCCESS);\n\t\t\t\tbreak;\n\t\t\tcase 'v':\n\t\t\t\tprintf(\"%s\\n\", VERSION);\n\t\t\t\texit(EXIT_SUCCESS);\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\t\tsnprintf(config_path, sizeof(config_path), \"%s\", optarg);\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\trun_level |= 1;\n\t\t\t\tsnprintf(state_path, sizeof(state_path), \"%s\", optarg);\n\t\t\t\tbreak;\n\t\t\tcase 'o':\n\t\t\t\trun_level |= 2;\n\t\t\t\tsock_fd = strtol(optarg, &end, 0);\n\t\t\t\tif (*end != '\\0') {\n\t\t\t\t\tsock_fd = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (config_path[0] == '\\0') {\n\t\tchar *config_home = getenv(CONFIG_HOME_ENV);\n\t\tif (config_home != NULL) {\n\t\t\tsnprintf(config_path, sizeof(config_path), \"%s/%s/%s\", config_home, WM_NAME, CONFIG_NAME);\n\t\t} else {\n\t\t\tsnprintf(config_path, sizeof(config_path), \"%s/%s/%s/%s\", getenv(\"HOME\"), \".config\", WM_NAME, CONFIG_NAME);\n\t\t}\n\t}\n\n\tdpy = xcb_connect(NULL, &default_screen);\n\n\tif (!check_connection(dpy)) {\n\t\texit(EXIT_FAILURE);\n\t}\n\n\tload_settings();\n\tsetup();\n\n\tif (state_path[0] != '\\0') {\n\t\trestore_state(state_path);\n\t\tunlink(state_path);\n\t}\n\n\tdpy_fd = xcb_get_file_descriptor(dpy);\n\n\tif (sock_fd == -1) {\n\t\tchar *sp = getenv(SOCKET_ENV_VAR);\n\t\tif (sp != NULL) {\n\t\t\tsnprintf(socket_path, sizeof(socket_path), \"%s\", sp);\n\t\t} else {\n\t\t\tchar *host = NULL;\n\t\t\tint dn = 0, sn = 0;\n\t\t\tif (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {\n\t\t\t\tsnprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);\n\t\t\t}\n\t\t\tfree(host);\n\t\t}\n\n\t\tsock_address.sun_family = AF_UNIX;\n\t\tif (snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), \"%s\", socket_path) < 0) {\n\t\t\terr(\"Couldn't write the socket path.\\n\");\n\t\t}\n\n\t\tsock_fd = socket(AF_UNIX, SOCK_STREAM, 0);\n\n\t\tif (sock_fd == -1) {\n\t\t\terr(\"Couldn't create the socket.\\n\");\n\t\t}\n\n\t\tunlink(socket_path);\n\n\t\tif (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {\n\t\t\terr(\"Couldn't bind a name to the socket.\\n\");\n\t\t}\n\n\t\tif (listen(sock_fd, SOMAXCONN) == -1) {\n\t\t\terr(\"Couldn't listen to the socket.\\n\");\n\t\t}\n\t}\n\n\tfcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));\n\n\tsigact.sa_handler = sig_handler;\n\tsigemptyset(&sigact.sa_mask);\n\tsigact.sa_flags = SA_RESTART;\n\tsigaction(SIGCHLD, &sigact, NULL);\n\tsigaction(SIGINT, &sigact, NULL);\n\tsigaction(SIGHUP, &sigact, NULL);\n\tsigaction(SIGTERM, &sigact, NULL);\n\t/* We avoid using SIG_IGN with SIGPIPE because that would be preserved across\n\t   exec. */\n\tsigaction(SIGPIPE, &sigact, NULL);\n\n\trun_config(run_level);\n\trunning = true;\n\n\twhile (running) {\n\n\t\txcb_flush(dpy);\n\n\t\tFD_ZERO(&descriptors);\n\t\tFD_SET(sock_fd, &descriptors);\n\t\tFD_SET(dpy_fd, &descriptors);\n\t\tmax_fd = MAX(sock_fd, dpy_fd);\n\n\t\tfor (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {\n\t\t\tFD_SET(pr->fd, &descriptors);\n\t\t\tif (pr->fd > max_fd) {\n\t\t\t\tmax_fd = pr->fd;\n\t\t\t}\n\t\t}\n\n\t\tif (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {\n\n\t\t\tpending_rule_t *pr = pending_rule_head;\n\t\t\twhile (pr != NULL) {\n\t\t\t\tpending_rule_t *next = pr->next;\n\t\t\t\tif (FD_ISSET(pr->fd, &descriptors)) {\n\t\t\t\t\tif (manage_window(pr->win, pr->csq, pr->fd)) {\n\t\t\t\t\t\tfor (event_queue_t *eq = pr->event_head; eq != NULL; eq = eq->next) {\n\t\t\t\t\t\t\thandle_event(&eq->event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tremove_pending_rule(pr);\n\t\t\t\t}\n\t\t\t\tpr = next;\n\t\t\t}\n\n\t\t\tif (FD_ISSET(sock_fd, &descriptors)) {\n\t\t\t\tcli_fd = accept(sock_fd, NULL, 0);\n\t\t\t\tif (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg)-1, 0)) > 0) {\n\t\t\t\t\tmsg[n] = '\\0';\n\t\t\t\t\tFILE *rsp = fdopen(cli_fd, \"w\");\n\t\t\t\t\tif (rsp != NULL) {\n\t\t\t\t\t\thandle_message(msg, n, rsp);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twarn(\"Can't open the client socket as file.\\n\");\n\t\t\t\t\t\tclose(cli_fd);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (FD_ISSET(dpy_fd, &descriptors)) {\n\t\t\t\txcb_aux_sync(dpy);\n\t\t\t\twhile ((event = xcb_poll_for_event(dpy)) != NULL) {\n\t\t\t\t\thandle_event(event);\n\t\t\t\t\tfree(event);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tif (!check_connection(dpy)) {\n\t\t\trunning = false;\n\t\t}\n\n\t\tprune_dead_subscribers();\n\t}\n\n\tif (restart) {\n\t\tchar *host = NULL;\n\t\tint dn = 0, sn = 0;\n\t\tif (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {\n\t\t\tsnprintf(state_path, sizeof(state_path), STATE_PATH_TPL, host, dn, sn);\n\t\t}\n\t\tfree(host);\n\t\tFILE *f = fopen(state_path, \"w\");\n\t\tquery_state(f);\n\t\tfclose(f);\n\t}\n\n\tcleanup();\n\tungrab_buttons();\n\txcb_ewmh_connection_wipe(ewmh);\n\txcb_destroy_window(dpy, meta_window);\n\txcb_destroy_window(dpy, motion_recorder.id);\n\tfree(ewmh);\n\txcb_flush(dpy);\n\txcb_disconnect(dpy);\n\n\tif (restart) {\n\t\tfcntl(sock_fd, F_SETFD, ~FD_CLOEXEC & fcntl(sock_fd, F_GETFD));\n\n\t\tint rargc;\n\t\tfor (rargc = 0; rargc < argc; rargc++) {\n\t\t\tif (streq(\"-s\", argv[rargc])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tint len = rargc + 5;\n\t\tchar **rargv = malloc(len * sizeof(char *));\n\n\t\tfor (int i = 0; i < rargc; i++) {\n\t\t\trargv[i] = argv[i];\n\t\t}\n\n\t\tchar sock_fd_arg[SMALEN];\n\t\tsnprintf(sock_fd_arg, sizeof(sock_fd_arg), \"%i\", sock_fd);\n\n\t\trargv[rargc] = \"-s\";\n\t\trargv[rargc + 1] = state_path;\n\t\trargv[rargc + 2] = \"-o\";\n\t\trargv[rargc + 3] = sock_fd_arg;\n\t\trargv[rargc + 4] = 0;\n\n\t\texecvp(*rargv, rargv);\n\n\t\texit_status = 1;\n\t\tfree(rargv);\n\t}\n\n\tclose(sock_fd);\n\tunlink(socket_path);\n\n\treturn exit_status;\n}\n\nvoid init(void)\n{\n\tclients_count = 0;\n\tmon = mon_head = mon_tail = pri_mon = NULL;\n\thistory_head = history_tail = history_needle = NULL;\n\trule_head = rule_tail = NULL;\n\tstack_head = stack_tail = NULL;\n\tsubscribe_head = subscribe_tail = NULL;\n\tpending_rule_head = pending_rule_tail = NULL;\n\tauto_raise = sticky_still = hide_sticky = record_history = true;\n\trandr_base = 0;\n\texit_status = 0;\n\trestart = false;\n}\n\nvoid setup(void)\n{\n\tinit();\n\tewmh_init();\n\tpointer_init();\n\n\tscreen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;\n\n\tif (screen == NULL) {\n\t\terr(\"Can't acquire the default screen.\\n\");\n\t}\n\n\troot = screen->root;\n\tregister_events();\n\n\tscreen_width = screen->width_in_pixels;\n\tscreen_height = screen->height_in_pixels;\n\n\tmeta_window = xcb_generate_id(dpy);\n\txcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);\n\txcb_icccm_set_wm_class(dpy, meta_window, sizeof(META_WINDOW_IC), META_WINDOW_IC);\n\n\tmotion_recorder.id = xcb_generate_id(dpy);\n\tmotion_recorder.sequence = 0;\n\tmotion_recorder.enabled = false;\n\tuint32_t values[] = {XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION};\n\txcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder.id, root, 0, 0, 1, 1, 0,\n\t                  XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);\n\txcb_icccm_set_wm_class(dpy, motion_recorder.id, sizeof(MOTION_RECORDER_IC), MOTION_RECORDER_IC);\n\n\txcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,\n\t                          ewmh->_NET_SUPPORTING_WM_CHECK,\n\t                          ewmh->_NET_DESKTOP_NAMES,\n\t                          ewmh->_NET_DESKTOP_VIEWPORT,\n\t                          ewmh->_NET_NUMBER_OF_DESKTOPS,\n\t                          ewmh->_NET_CURRENT_DESKTOP,\n\t                          ewmh->_NET_CLIENT_LIST,\n\t                          ewmh->_NET_ACTIVE_WINDOW,\n\t                          ewmh->_NET_CLOSE_WINDOW,\n\t                          ewmh->_NET_WM_STRUT_PARTIAL,\n\t                          ewmh->_NET_WM_DESKTOP,\n\t                          ewmh->_NET_WM_STATE,\n\t                          ewmh->_NET_WM_STATE_HIDDEN,\n\t                          ewmh->_NET_WM_STATE_FULLSCREEN,\n\t                          ewmh->_NET_WM_STATE_BELOW,\n\t                          ewmh->_NET_WM_STATE_ABOVE,\n\t                          ewmh->_NET_WM_STATE_STICKY,\n\t                          ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,\n\t                          ewmh->_NET_WM_WINDOW_TYPE,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_DOCK,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_DIALOG,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_UTILITY,\n\t                          ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};\n\n\txcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);\n\tewmh_set_supporting(meta_window);\n\n#define GETATOM(a) \\\n\tget_atom(#a, &a);\n\tGETATOM(WM_STATE)\n\tGETATOM(WM_DELETE_WINDOW)\n\tGETATOM(WM_TAKE_FOCUS)\n#undef GETATOM\n\n\tconst xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);\n\tif (qep->present && update_monitors()) {\n\t\trandr = true;\n\t\trandr_base = qep->first_event;\n\t\txcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);\n\t} else {\n\t\trandr = false;\n\t\twarn(\"Couldn't retrieve monitors via RandR.\\n\");\n\t\tbool xinerama_is_active = false;\n\n\t\tif (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {\n\t\t\txcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);\n\t\t\tif (xia != NULL) {\n\t\t\t\txinerama_is_active = xia->state;\n\t\t\t\tfree(xia);\n\t\t\t}\n\t\t}\n\n\t\tif (xinerama_is_active) {\n\t\t\txcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);\n\t\t\txcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);\n\t\t\tint n = xcb_xinerama_query_screens_screen_info_length(xsq);\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\txcb_xinerama_screen_info_t info = xsi[i];\n\t\t\t\txcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};\n\t\t\t\tmonitor_t *m = make_monitor(NULL, &rect, XCB_NONE);\n\t\t\t\tadd_monitor(m);\n\t\t\t\tadd_desktop(m, make_desktop(NULL, XCB_NONE));\n\t\t\t}\n\t\t\tfree(xsq);\n\t\t} else {\n\t\t\twarn(\"Xinerama is inactive.\\n\");\n\t\t\txcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};\n\t\t\tmonitor_t *m = make_monitor(NULL, &rect, XCB_NONE);\n\t\t\tadd_monitor(m);\n\t\t\tadd_desktop(m, make_desktop(NULL, XCB_NONE));\n\t\t}\n\t}\n\n\tewmh_update_number_of_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_current_desktop();\n\txcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);\n\tif (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {\n\t\tclear_input_focus();\n\t}\n\tfree(ifo);\n}\n\nvoid register_events(void)\n{\n\tuint32_t values[] = {ROOT_EVENT_MASK};\n\txcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));\n\tif (e != NULL) {\n\t\tfree(e);\n\t\txcb_ewmh_connection_wipe(ewmh);\n\t\tfree(ewmh);\n\t\txcb_disconnect(dpy);\n\t\terr(\"Another window manager is already running.\\n\");\n\t}\n}\n\nvoid cleanup(void)\n{\n\tmon = NULL;\n\n\twhile (mon_head != NULL) {\n\t\tremove_monitor(mon_head);\n\t}\n\twhile (rule_head != NULL) {\n\t\tremove_rule(rule_head);\n\t}\n\twhile (subscribe_head != NULL) {\n\t\tremove_subscriber(subscribe_head);\n\t}\n\twhile (pending_rule_head != NULL) {\n\t\tremove_pending_rule(pending_rule_head);\n\t}\n\n\tempty_history();\n}\n\nbool check_connection (xcb_connection_t *dpy)\n{\n\tint xerr;\n\tif ((xerr = xcb_connection_has_error(dpy)) != 0) {\n\t\twarn(\"The server closed the connection: \");\n\t\tswitch (xerr) {\n\t\t\tcase XCB_CONN_ERROR:\n\t\t\t\twarn(\"socket, pipe or stream error.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_EXT_NOTSUPPORTED:\n\t\t\t\twarn(\"unsupported extension.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_MEM_INSUFFICIENT:\n\t\t\t\twarn(\"not enough memory.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_REQ_LEN_EXCEED:\n\t\t\t\twarn(\"request length exceeded.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_PARSE_ERR:\n\t\t\t\twarn(\"can't parse display string.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_INVALID_SCREEN:\n\t\t\t\twarn(\"invalid screen.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase XCB_CONN_CLOSED_FDPASSING_FAILED:\n\t\t\t\twarn(\"failed to pass FD.\\n\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\twarn(\"unknown error.\\n\");\n\t\t\t\tbreak;\n\t\t}\n\t\treturn false;\n\t} else {\n\t\treturn true;\n\t}\n}\n\nvoid sig_handler(int sig)\n{\n\tif (sig == SIGCHLD) {\n\t\twhile (waitpid(-1, 0, WNOHANG) > 0)\n\t\t\t;\n\t} else if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {\n\t\trunning = false;\n\t}\n}\n\n/* Adapted from i3wm */\nuint32_t get_color_pixel(const char *color)\n{\n\tunsigned int red, green, blue;\n\tif (sscanf(color + 1, \"%02x%02x%02x\", &red, &green, &blue) == 3) {\n\t\t/* We set the first 8 bits high to have 100% opacity in case of a 32 bit\n\t\t * color depth visual. */\n\t\treturn (0xFF << 24) | (red << 16 | green << 8 | blue);\n\t} else {\n\t\treturn screen->black_pixel;\n\t}\n}\n"
  },
  {
    "path": "src/bspwm.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_BSPWM_H\n#define BSPWM_BSPWM_H\n\n#include <signal.h>\n#include \"types.h\"\n\n#define WM_NAME                  \"bspwm\"\n#define CONFIG_NAME              WM_NAME \"rc\"\n#define CONFIG_HOME_ENV          \"XDG_CONFIG_HOME\"\n#define RUNTIME_DIR_ENV          \"XDG_RUNTIME_DIR\"\n\n#define STATE_PATH_TPL           \"/tmp/bspwm%s_%i_%i-state\"\n\n#define ROOT_EVENT_MASK     (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_FOCUS_CHANGE)\n#define CLIENT_EVENT_MASK   (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)\n#define BSPWM_CLASS_NAME    \"Bspwm\"\n#define META_WINDOW_IC      \"wm\\0\" BSPWM_CLASS_NAME\n#define ROOT_WINDOW_IC      \"root\\0\" BSPWM_CLASS_NAME\n#define PRESEL_FEEDBACK_I   \"presel_feedback\"\n#define PRESEL_FEEDBACK_IC  PRESEL_FEEDBACK_I \"\\0\" BSPWM_CLASS_NAME\n#define MOTION_RECORDER_I   \"motion_recorder\"\n#define MOTION_RECORDER_IC  MOTION_RECORDER_I \"\\0\" BSPWM_CLASS_NAME\n\ntypedef struct {\n\txcb_window_t id;\n\tuint16_t sequence;\n\tbool enabled;\n} motion_recorder_t;\n\nextern xcb_connection_t *dpy;\nextern int default_screen, screen_width, screen_height;\nextern uint32_t clients_count;\nextern xcb_screen_t *screen;\nextern xcb_window_t root;\nextern char config_path[MAXLEN];\n\nextern monitor_t *mon;\nextern monitor_t *mon_head;\nextern monitor_t *mon_tail;\nextern monitor_t *pri_mon;\nextern history_t *history_head;\nextern history_t *history_tail;\nextern history_t *history_needle;\nextern rule_t *rule_head;\nextern rule_t *rule_tail;\nextern stacking_list_t *stack_head;\nextern stacking_list_t *stack_tail;\nextern subscriber_list_t *subscribe_head;\nextern subscriber_list_t *subscribe_tail;\nextern pending_rule_t *pending_rule_head;\nextern pending_rule_t *pending_rule_tail;\n\nextern xcb_window_t meta_window;\nextern motion_recorder_t motion_recorder;\nextern xcb_atom_t WM_STATE;\nextern xcb_atom_t WM_TAKE_FOCUS;\nextern xcb_atom_t WM_DELETE_WINDOW;\nextern int exit_status;\n\nextern bool auto_raise;\nextern bool sticky_still;\nextern bool hide_sticky;\nextern bool record_history;\nextern volatile sig_atomic_t running;\nextern bool restart;\nextern bool randr;\n\nvoid init(void);\nvoid setup(void);\nvoid register_events(void);\nvoid cleanup(void);\nbool check_connection (xcb_connection_t *dpy);\nvoid sig_handler(int sig);\nuint32_t get_color_pixel(const char *color);\n\n#endif\n"
  },
  {
    "path": "src/common.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_COMMON_H\n#define BSPWM_COMMON_H\n\n#define SOCKET_PATH_TPL  \"/tmp/bspwm%s_%i_%i-socket\"\n#define SOCKET_ENV_VAR   \"BSPWM_SOCKET\"\n\n#define FAILURE_MESSAGE  \"\\x07\"\n\n#endif\n"
  },
  {
    "path": "src/desktop.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include \"bspwm.h\"\n#include \"ewmh.h\"\n#include \"history.h\"\n#include \"monitor.h\"\n#include \"query.h\"\n#include \"tree.h\"\n#include \"window.h\"\n#include \"desktop.h\"\n#include \"subscribe.h\"\n#include \"settings.h\"\n\nbool activate_desktop(monitor_t *m, desktop_t *d)\n{\n\tif (d != NULL && m == mon) {\n\t\treturn false;\n\t}\n\n\tif (d == NULL) {\n\t\td = m->desk;\n\t\tif (d == NULL) {\n\t\t\td = history_last_desktop(m, NULL);\n\t\t}\n\t\tif (d == NULL) {\n\t\t\td = m->desk_head;\n\t\t}\n\t}\n\n\tif (d == NULL || d == m->desk) {\n\t\treturn false;\n\t}\n\n\tif (m->sticky_count > 0 && m->desk != NULL) {\n\t\ttransfer_sticky_nodes(m, m->desk, m, d, m->desk->root);\n\t}\n\n\tshow_desktop(d);\n\thide_desktop(m->desk);\n\n\tm->desk = d;\n\n\thistory_add(m, d, NULL, false);\n\n\tput_status(SBSC_MASK_DESKTOP_ACTIVATE, \"desktop_activate 0x%08X 0x%08X\\n\", m->id, d->id);\n\tput_status(SBSC_MASK_REPORT);\n\n\treturn true;\n}\n\nbool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t *sel)\n{\n\tmonitor_t *m = ref->monitor;\n\tdesktop_t *d = ref->desktop;\n\td = (dir == CYCLE_PREV ? d->prev : d->next);\n\n#define HANDLE_BOUNDARIES(m, d)  \\\n\tif (d == NULL) { \\\n\t\tm = (dir == CYCLE_PREV ? m->prev : m->next); \\\n\t\tif (m == NULL) { \\\n\t\t\tm = (dir == CYCLE_PREV ? mon_tail : mon_head); \\\n\t\t} \\\n\t\td = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \\\n\t}\n\tHANDLE_BOUNDARIES(m, d)\n\n\twhile (d != ref->desktop) {\n\t\tcoordinates_t loc = {m, d, NULL};\n\t\tif (desktop_matches(&loc, ref, sel)) {\n\t\t\t*dst = loc;\n\t\t\treturn true;\n\t\t}\n\t\td = (dir == CYCLE_PREV ? d->prev : d->next);\n\t\tHANDLE_BOUNDARIES(m, d)\n\t}\n#undef HANDLE_BOUNDARIES\n\n\treturn false;\n}\n\nbool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tcoordinates_t loc = {m, d, NULL};\n\t\t\tif (desktop_matches(&loc, ref, sel)) {\n\t\t\t\t*dst = loc;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool set_layout(monitor_t *m, desktop_t *d, layout_t l, bool user)\n{\n\tif ((user && d->user_layout == l) || (!user && d->layout == l)) {\n\t\treturn false;\n\t}\n\n\tlayout_t old_layout = d->layout;\n\n\tif (user) {\n\t\td->user_layout = l;\n\t} else {\n\t\td->layout = l;\n\t}\n\n\tif (user && (!single_monocle || tiled_count(d->root, true) > 1)) {\n\t\td->layout = l;\n\t}\n\n\tif (d->layout != old_layout) {\n\t\thandle_presel_feedbacks(m, d);\n\n\t\tif (user) {\n\t\t\tarrange(m, d);\n\t\t}\n\n\t\tput_status(SBSC_MASK_DESKTOP_LAYOUT, \"desktop_layout 0x%08X 0x%08X %s\\n\", m->id, d->id, LAYOUT_STR(d->layout));\n\n\t\tif (d == m->desk) {\n\t\t\tput_status(SBSC_MASK_REPORT);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid handle_presel_feedbacks(monitor_t *m, desktop_t *d)\n{\n\tif (m->desk != d) {\n\t\treturn;\n\t}\n\tif (d->layout == LAYOUT_MONOCLE) {\n\t\thide_presel_feedbacks(m, d, d->root);\n\t} else {\n\t\tshow_presel_feedbacks(m, d, d->root);\n\t}\n}\n\nbool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d, bool follow)\n{\n\tif (ms == NULL || md == NULL || d == NULL || ms == md) {\n\t\treturn false;\n\t}\n\n\tbool d_was_active = (d == ms->desk);\n\tbool ms_was_focused = (ms == mon);\n\tunsigned int sc = (ms->sticky_count > 0 && d_was_active) ? sticky_count(d->root) : 0;\n\n\tunlink_desktop(ms, d);\n\tms->sticky_count -= sc;\n\n\tif ((!follow || !d_was_active || !ms_was_focused) && md->desk != NULL) {\n\t\thide_sticky = false;\n\t\thide_desktop(d);\n\t\thide_sticky = true;\n\t}\n\n\tinsert_desktop(md, d);\n\tmd->sticky_count += sc;\n\thistory_remove(d, NULL, false);\n\n\tif (d_was_active) {\n\t\tif (follow) {\n\t\t\tif (activate_desktop(ms, NULL)) {\n\t\t\t\tactivate_node(ms, ms->desk, NULL);\n\t\t\t}\n\t\t\tif (ms_was_focused) {\n\t\t\t\tfocus_node(md, d, d->focus);\n\t\t\t}\n\t\t} else {\n\t\t\tif (ms_was_focused) {\n\t\t\t\tfocus_node(ms, ms->desk, NULL);\n\t\t\t} else if (activate_desktop(ms, NULL)) {\n\t\t\t\tactivate_node(ms, ms->desk, NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (sc > 0) {\n\t\tif (ms->desk != NULL) {\n\t\t\ttransfer_sticky_nodes(md, d, ms, ms->desk, d->root);\n\t\t} else if (d != md->desk) {\n\t\t\ttransfer_sticky_nodes(md, d, md, md->desk, d->root);\n\t\t}\n\t}\n\n\tadapt_geometry(&ms->rectangle, &md->rectangle, d->root);\n\tarrange(md, d);\n\n\tif ((!follow || !d_was_active || !ms_was_focused) && md->desk == d) {\n\t\tif (md == mon) {\n\t\t\tfocus_node(md, d, d->focus);\n\t\t} else {\n\t\t\tactivate_node(md, d, d->focus);\n\t\t}\n\t}\n\n\tewmh_update_wm_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_current_desktop();\n\n\tput_status(SBSC_MASK_DESKTOP_TRANSFER, \"desktop_transfer 0x%08X 0x%08X 0x%08X\\n\", ms->id, d->id, md->id);\n\tput_status(SBSC_MASK_REPORT);\n\n\treturn true;\n}\n\ndesktop_t *make_desktop(const char *name, uint32_t id)\n{\n\tdesktop_t *d = calloc(1, sizeof(desktop_t));\n\tsnprintf(d->name, sizeof(d->name), \"%s\", name == NULL ? DEFAULT_DESK_NAME : name);\n\tif (id == XCB_NONE) {\n\t\td->id = xcb_generate_id(dpy);\n\t}\n\td->prev = d->next = NULL;\n\td->root = d->focus = NULL;\n\td->user_layout = LAYOUT_TILED;\n\td->layout = single_monocle ? LAYOUT_MONOCLE : LAYOUT_TILED;\n\td->padding = (padding_t) PADDING;\n\td->window_gap = window_gap;\n\td->border_width = border_width;\n\treturn d;\n}\n\nvoid rename_desktop(monitor_t *m, desktop_t *d, const char *name)\n{\n\n\tput_status(SBSC_MASK_DESKTOP_RENAME, \"desktop_rename 0x%08X 0x%08X %s %s\\n\", m->id, d->id, d->name, name);\n\n\tsnprintf(d->name, sizeof(d->name), \"%s\", name);\n\n\tput_status(SBSC_MASK_REPORT);\n\tewmh_update_desktop_names();\n}\n\nvoid insert_desktop(monitor_t *m, desktop_t *d)\n{\n\tif (m->desk == NULL) {\n\t\tm->desk = d;\n\t\tm->desk_head = d;\n\t\tm->desk_tail = d;\n\t} else {\n\t\tm->desk_tail->next = d;\n\t\td->prev = m->desk_tail;\n\t\tm->desk_tail = d;\n\t}\n}\n\nvoid add_desktop(monitor_t *m, desktop_t *d)\n{\n\tput_status(SBSC_MASK_DESKTOP_ADD, \"desktop_add 0x%08X 0x%08X %s\\n\", m->id, d->id, d->name);\n\n\td->border_width = m->border_width;\n\td->window_gap = m->window_gap;\n\tinsert_desktop(m, d);\n\tewmh_update_current_desktop();\n\tewmh_update_number_of_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_wm_desktops();\n\tput_status(SBSC_MASK_REPORT);\n}\n\ndesktop_t *find_desktop_in(uint32_t id, monitor_t *m)\n{\n\tif (m == NULL) {\n\t\treturn NULL;\n\t}\n\n\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\tif (d->id == id) {\n\t\t\treturn d;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid unlink_desktop(monitor_t *m, desktop_t *d)\n{\n\tdesktop_t *prev = d->prev;\n\tdesktop_t *next = d->next;\n\n\tif (prev != NULL) {\n\t\tprev->next = next;\n\t}\n\n\tif (next != NULL) {\n\t\tnext->prev = prev;\n\t}\n\n\tif (m->desk_head == d) {\n\t\tm->desk_head = next;\n\t}\n\n\tif (m->desk_tail == d) {\n\t\tm->desk_tail = prev;\n\t}\n\n\tif (m->desk == d) {\n\t\tm->desk = NULL;\n\t}\n\n\td->prev = d->next = NULL;\n}\n\nvoid remove_desktop(monitor_t *m, desktop_t *d)\n{\n\tput_status(SBSC_MASK_DESKTOP_REMOVE, \"desktop_remove 0x%08X 0x%08X\\n\", m->id, d->id);\n\n\tremove_node(m, d, d->root);\n\tunlink_desktop(m, d);\n\thistory_remove(d, NULL, false);\n\tfree(d);\n\n\tewmh_update_current_desktop();\n\tewmh_update_number_of_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\n\tif (mon != NULL && m->desk == NULL) {\n\t\tif (m == mon) {\n\t\t\tfocus_node(m, NULL, NULL);\n\t\t} else {\n\t\t\tactivate_desktop(m, NULL);\n\t\t\tif (m->desk != NULL) {\n\t\t\t\tactivate_node(m, m->desk, m->desk->focus);\n\t\t\t}\n\t\t}\n\t}\n\n\tput_status(SBSC_MASK_REPORT);\n}\n\nvoid merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)\n{\n\tif (ds == NULL || dd == NULL || ds == dd) {\n\t\treturn;\n\t}\n\t/* TODO: Handle sticky nodes. */\n\ttransfer_node(ms, ds, ds->root, md, dd, dd->focus, false);\n}\n\nbool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2, bool follow)\n{\n\tif (d1 == NULL || d2 == NULL || d1 == d2) {\n\t\treturn false;\n\t}\n\n\tput_status(SBSC_MASK_DESKTOP_SWAP, \"desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\\n\", m1->id, d1->id, m2->id, d2->id);\n\n\tbool d1_was_active = (m1->desk == d1);\n\tbool d2_was_active = (m2->desk == d2);\n\tbool d1_was_focused = (mon->desk == d1);\n\tbool d2_was_focused = (mon->desk == d2);\n\tdesktop_t *d1_stickies = NULL;\n\tdesktop_t *d2_stickies = NULL;\n\n\tif (m1->sticky_count > 0 && d1 == m1->desk && sticky_count(d1->root) > 0) {\n\t\td1_stickies = make_desktop(NULL, XCB_NONE);\n\t\tinsert_desktop(m1, d1_stickies);\n\t\ttransfer_sticky_nodes(m1, d1, m1, d1_stickies, d1->root);\n\t}\n\n\tif (m2->sticky_count > 0 && d2 == m2->desk && sticky_count(d2->root) > 0) {\n\t\td2_stickies = make_desktop(NULL, XCB_NONE);\n\t\tinsert_desktop(m2, d2_stickies);\n\t\ttransfer_sticky_nodes(m2, d2, m2, d2_stickies, d2->root);\n\t}\n\n\tif (m1 != m2) {\n\t\tif (m1->desk == d1) {\n\t\t\tm1->desk = d2;\n\t\t}\n\t\tif (m1->desk_head == d1) {\n\t\t\tm1->desk_head = d2;\n\t\t}\n\t\tif (m1->desk_tail == d1) {\n\t\t\tm1->desk_tail = d2;\n\t\t}\n\t\tif (m2->desk == d2) {\n\t\t\tm2->desk = d1;\n\t\t}\n\t\tif (m2->desk_head == d2) {\n\t\t\tm2->desk_head = d1;\n\t\t}\n\t\tif (m2->desk_tail == d2) {\n\t\t\tm2->desk_tail = d1;\n\t\t}\n\t} else {\n\t\tif (m1->desk == d1) {\n\t\t\tm1->desk = d2;\n\t\t} else if (m1->desk == d2) {\n\t\t\tm1->desk = d1;\n\t\t}\n\t\tif (m1->desk_head == d1) {\n\t\t\tm1->desk_head = d2;\n\t\t} else if (m1->desk_head == d2) {\n\t\t\tm1->desk_head = d1;\n\t\t}\n\t\tif (m1->desk_tail == d1) {\n\t\t\tm1->desk_tail = d2;\n\t\t} else if (m1->desk_tail == d2) {\n\t\t\tm1->desk_tail = d1;\n\t\t}\n\t}\n\n\tdesktop_t *p1 = d1->prev;\n\tdesktop_t *n1 = d1->next;\n\tdesktop_t *p2 = d2->prev;\n\tdesktop_t *n2 = d2->next;\n\n\tif (p1 != NULL && p1 != d2) {\n\t\tp1->next = d2;\n\t}\n\tif (n1 != NULL && n1 != d2) {\n\t\tn1->prev = d2;\n\t}\n\tif (p2 != NULL && p2 != d1) {\n\t\tp2->next = d1;\n\t}\n\tif (n2 != NULL && n2 != d1) {\n\t\tn2->prev = d1;\n\t}\n\n\td1->prev = p2 == d1 ? d2 : p2;\n\td1->next = n2 == d1 ? d2 : n2;\n\td2->prev = p1 == d2 ? d1 : p1;\n\td2->next = n1 == d2 ? d1 : n1;\n\n\tif (m1 != m2) {\n\t\tadapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);\n\t\tadapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);\n\t\thistory_remove(d1, NULL, false);\n\t\thistory_remove(d2, NULL, false);\n\t\tarrange(m1, d2);\n\t\tarrange(m2, d1);\n\t}\n\n\tif (d1_stickies != NULL) {\n\t\ttransfer_sticky_nodes(m1, d1_stickies, m1, d2, d1_stickies->root);\n\t\tunlink_desktop(m1, d1_stickies);\n\t\tfree(d1_stickies);\n\t}\n\n\tif (d2_stickies != NULL) {\n\t\ttransfer_sticky_nodes(m2, d2_stickies, m2, d1, d2_stickies->root);\n\t\tunlink_desktop(m2, d2_stickies);\n\t\tfree(d2_stickies);\n\t}\n\n\tif (d1_was_active && !d2_was_active) {\n\t\tif ((!follow && m1 != m2) || !d1_was_focused) {\n\t\t\thide_desktop(d1);\n\t\t}\n\t\tshow_desktop(d2);\n\t} else if (!d1_was_active && d2_was_active) {\n\t\tshow_desktop(d1);\n\t\tif ((!follow && m1 != m2) || !d2_was_focused) {\n\t\t\thide_desktop(d2);\n\t\t}\n\t}\n\n\tif (follow || m1 == m2) {\n\t\tif (d1_was_focused) {\n\t\t\tfocus_node(m2, d1, d1->focus);\n\t\t} else if (d1_was_active) {\n\t\t\tactivate_node(m2, d1, d1->focus);\n\t\t}\n\n\t\tif (d2_was_focused) {\n\t\t\tfocus_node(m1, d2, d2->focus);\n\t\t} else if (d2_was_active) {\n\t\t\tactivate_node(m1, d2, d2->focus);\n\t\t}\n\t} else {\n\t\tif (d1_was_focused) {\n\t\t\tfocus_node(m1, d2, d2->focus);\n\t\t} else if (d1_was_active) {\n\t\t\tactivate_node(m1, d2, d2->focus);\n\t\t}\n\n\t\tif (d2_was_focused) {\n\t\t\tfocus_node(m2, d1, d1->focus);\n\t\t} else if (d2_was_active) {\n\t\t\tactivate_node(m2, d1, d1->focus);\n\t\t}\n\t}\n\n\tewmh_update_wm_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_current_desktop();\n\n\tput_status(SBSC_MASK_REPORT);\n\n\treturn true;\n}\n\nvoid show_desktop(desktop_t *d)\n{\n\tif (d == NULL) {\n\t\treturn;\n\t}\n\tshow_node(d, d->root);\n}\n\nvoid hide_desktop(desktop_t *d)\n{\n\tif (d == NULL) {\n\t\treturn;\n\t}\n\thide_node(d, d->root);\n}\n\nbool is_urgent(desktop_t *d)\n{\n\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\tif (n->client == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (n->client->urgent) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "src/desktop.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_DESKTOP_H\n#define BSPWM_DESKTOP_H\n\n#define DEFAULT_DESK_NAME    \"Desktop\"\n\nbool activate_desktop(monitor_t *m, desktop_t *d);\nbool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t *sel);\nbool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel);\nbool set_layout(monitor_t *m, desktop_t *d, layout_t l, bool user);\nvoid handle_presel_feedbacks(monitor_t *m, desktop_t *d);\nbool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d, bool follow);\ndesktop_t *make_desktop(const char *name, uint32_t id);\nvoid rename_desktop(monitor_t *m, desktop_t *d, const char *name);\nvoid insert_desktop(monitor_t *m, desktop_t *d);\nvoid add_desktop(monitor_t *m, desktop_t *d);\ndesktop_t *find_desktop_in(uint32_t id, monitor_t *m);\nvoid unlink_desktop(monitor_t *m, desktop_t *d);\nvoid remove_desktop(monitor_t *m, desktop_t *d);\nvoid merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);\nbool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2, bool follow);\nvoid show_desktop(desktop_t *d);\nvoid hide_desktop(desktop_t *d);\nbool is_urgent(desktop_t *d);\n\n#endif\n"
  },
  {
    "path": "src/events.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdbool.h>\n#include \"bspwm.h\"\n#include \"ewmh.h\"\n#include \"monitor.h\"\n#include \"query.h\"\n#include \"settings.h\"\n#include \"subscribe.h\"\n#include \"tree.h\"\n#include \"window.h\"\n#include \"pointer.h\"\n#include \"rule.h\"\n#include \"events.h\"\n\nuint8_t randr_base;\n\nvoid handle_event(xcb_generic_event_t *evt)\n{\n\tuint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);\n\tswitch (resp_type) {\n\t\tcase XCB_MAP_REQUEST:\n\t\t\tmap_request(evt);\n\t\t\tbreak;\n\t\tcase XCB_DESTROY_NOTIFY:\n\t\t\tdestroy_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_UNMAP_NOTIFY:\n\t\t\tunmap_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_CLIENT_MESSAGE:\n\t\t\tclient_message(evt);\n\t\t\tbreak;\n\t\tcase XCB_CONFIGURE_REQUEST:\n\t\t\tconfigure_request(evt);\n\t\t\tbreak;\n\t\tcase XCB_CONFIGURE_NOTIFY:\n\t\t\tconfigure_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_PROPERTY_NOTIFY:\n\t\t\tproperty_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_ENTER_NOTIFY:\n\t\t\tenter_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_MOTION_NOTIFY:\n\t\t\tmotion_notify(evt);\n\t\t\tbreak;\n\t\tcase XCB_BUTTON_PRESS:\n\t\t\tbutton_press(evt);\n\t\t\tbreak;\n\t\tcase XCB_FOCUS_IN:\n\t\t\tfocus_in(evt);\n\t\t\tbreak;\n\t\tcase XCB_MAPPING_NOTIFY:\n\t\t\tmapping_notify(evt);\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tprocess_error(evt);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {\n\t\t\t\tupdate_monitors();\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\nvoid map_request(xcb_generic_event_t *evt)\n{\n\txcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;\n\n\tschedule_window(e->window);\n}\n\nvoid configure_request(xcb_generic_event_t *evt)\n{\n\txcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;\n\n\tcoordinates_t loc;\n\tbool is_managed = locate_window(e->window, &loc);\n\tclient_t *c = (is_managed ? loc.node->client : NULL);\n\tuint16_t width, height;\n\n\tif (!is_managed) {\n\t\tuint16_t mask = 0;\n\t\tuint32_t values[7];\n\t\tunsigned short i = 0;\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_X) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_X;\n\t\t\tvalues[i++] = e->x;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_Y) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_Y;\n\t\t\tvalues[i++] = e->y;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_WIDTH;\n\t\t\tvalues[i++] = e->width;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_HEIGHT;\n\t\t\tvalues[i++] = e->height;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;\n\t\t\tvalues[i++] = e->border_width;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_SIBLING;\n\t\t\tvalues[i++] = e->sibling;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {\n\t\t\tmask |= XCB_CONFIG_WINDOW_STACK_MODE;\n\t\t\tvalues[i++] = e->stack_mode;\n\t\t}\n\n\t\txcb_configure_window(dpy, e->window, mask, values);\n\n\t} else if (IS_FLOATING(c)) {\n\t\twidth = c->floating_rectangle.width;\n\t\theight = c->floating_rectangle.height;\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_X) {\n\t\t\tc->floating_rectangle.x = e->x;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_Y) {\n\t\t\tc->floating_rectangle.y = e->y;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {\n\t\t\twidth = e->width;\n\t\t}\n\n\t\tif (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {\n\t\t\theight = e->height;\n\t\t}\n\n\t\tapply_size_hints(c, &width, &height);\n\t\tc->floating_rectangle.width = width;\n\t\tc->floating_rectangle.height = height;\n\t\txcb_rectangle_t r = c->floating_rectangle;\n\n\t\tr.x -= c->border_width;\n\t\tr.y -= c->border_width;\n\n\t\twindow_move_resize(e->window, r.x, r.y, r.width, r.height);\n\n\t\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y);\n\n\t\tmonitor_t *m = monitor_from_client(c);\n\t\tif (m != loc.monitor) {\n\t\t\ttransfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus, false);\n\t\t}\n\t} else {\n\t\tif (c->state == STATE_PSEUDO_TILED) {\n\t\t\twidth = c->floating_rectangle.width;\n\t\t\theight = c->floating_rectangle.height;\n\t\t\tif (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {\n\t\t\t\twidth = e->width;\n\t\t\t}\n\t\t\tif (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {\n\t\t\t\theight = e->height;\n\t\t\t}\n\t\t\tapply_size_hints(c, &width, &height);\n\t\t\tif (width != c->floating_rectangle.width || height != c->floating_rectangle.height) {\n\t\t\t\tc->floating_rectangle.width = width;\n\t\t\t\tc->floating_rectangle.height = height;\n\t\t\t\tarrange(loc.monitor, loc.desktop);\n\t\t\t}\n\t\t}\n\n\n\t\txcb_configure_notify_event_t evt;\n\t\tunsigned int bw = c->border_width;\n\n\t\txcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle;\n\n\t\tevt.response_type = XCB_CONFIGURE_NOTIFY;\n\t\tevt.event = e->window;\n\t\tevt.window = e->window;\n\t\tevt.above_sibling = XCB_NONE;\n\t\tevt.x = r.x;\n\t\tevt.y = r.y;\n\t\tevt.width = r.width;\n\t\tevt.height = r.height;\n\t\tevt.border_width = bw;\n\t\tevt.override_redirect = false;\n\n\t\txcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);\n\t}\n}\n\nvoid configure_notify(xcb_generic_event_t *evt)\n{\n\txcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *) evt;\n\n\tif (e->window == root) {\n\t\tscreen_width = e->width;\n\t\tscreen_height = e->height;\n\t}\n}\n\nvoid destroy_notify(xcb_generic_event_t *evt)\n{\n\txcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;\n\n\tunmanage_window(e->window);\n}\n\nvoid unmap_notify(xcb_generic_event_t *evt)\n{\n\txcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;\n\n\tif (e->window == motion_recorder.id) {\n\t\t/* Unmapping the motion recorder in `query_pointer` will produce\n\t\t * unwanted enter notify events. We can filter those events because\n\t\t * their sequence number is the same as the sequence number of the\n\t\t * related unmap notify event. This is a technique used by i3-wm to\n\t\t * filter enter notify events. */\n\t\tmotion_recorder.sequence = e->sequence;\n\t\treturn;\n\t}\n\n\t/* Filter out destroyed windows */\n\tif (!window_exists(e->window)) {\n\t\treturn;\n\t}\n\n\tset_window_state(e->window, XCB_ICCCM_WM_STATE_WITHDRAWN);\n\tunmanage_window(e->window);\n}\n\nvoid property_notify(xcb_generic_event_t *evt)\n{\n\txcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;\n\n\tif (!ignore_ewmh_struts && e->atom == ewmh->_NET_WM_STRUT_PARTIAL && ewmh_handle_struts(e->window)) {\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\t\tarrange(m, d);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {\n\t\treturn;\n\t}\n\n\tcoordinates_t loc;\n\tif (!locate_window(e->window, &loc)) {\n\t\tfor (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {\n\t\t\tif (pr->win == e->window) {\n\t\t\t\tpostpone_event(pr, evt);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (e->atom == XCB_ATOM_WM_HINTS) {\n\t\txcb_icccm_wm_hints_t hints;\n\t\tif (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&\n\t\t    (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))\n\t\t\tset_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));\n\t} else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {\n\t\tclient_t *c = loc.node->client;\n\t\tif (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) {\n\t\t\tarrange(loc.monitor, loc.desktop);\n\t\t}\n\t}\n}\n\nvoid client_message(xcb_generic_event_t *evt)\n{\n\txcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;\n\n\tif (e->type == ewmh->_NET_CURRENT_DESKTOP) {\n\t\tcoordinates_t loc;\n\t\tif (ewmh_locate_desktop(e->data.data32[0], &loc)) {\n\t\t\tfocus_node(loc.monitor, loc.desktop, loc.desktop->focus);\n\t\t}\n\t\treturn;\n\t}\n\n\tcoordinates_t loc;\n\tif (!locate_window(e->window, &loc)) {\n\t\tfor (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {\n\t\t\tif (pr->win == e->window) {\n\t\t\t\tpostpone_event(pr, evt);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (e->type == ewmh->_NET_WM_STATE) {\n\t\thandle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);\n\t\thandle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);\n\t} else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {\n\t\tif ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||\n\t\t    loc.node == mon->desk->focus) {\n\t\t\treturn;\n\t\t}\n\t\tfocus_node(loc.monitor, loc.desktop, loc.node);\n\t} else if (e->type == ewmh->_NET_WM_DESKTOP) {\n\t\tcoordinates_t dloc;\n\t\tif (ewmh_locate_desktop(e->data.data32[0], &dloc)) {\n\t\t\ttransfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus, false);\n\t\t}\n\t} else if (e->type == ewmh->_NET_CLOSE_WINDOW) {\n\t\tclose_node(loc.node);\n\t}\n}\n\nvoid focus_in(xcb_generic_event_t *evt)\n{\n\txcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;\n\n\tif (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB\n\t    || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT\n\t    || e->detail == XCB_NOTIFY_DETAIL_NONE) {\n\t\treturn;\n\t}\n\n\tif (mon->desk->focus != NULL) {\n\t\tif (e->event == mon->desk->focus->id) {\n\t\t\treturn;\n\t\t}\n\t\tif (e->event == root) {\n\t\t\t/* Some clients expect the window manager to refocus the\n\t\t\t   focused window in this case */\n\t\t\tbool pff = pointer_follows_focus;\n\t\t\tpointer_follows_focus = false;\n\t\t\tfocus_node(mon, mon->desk, mon->desk->focus);\n\t\t\tpointer_follows_focus = pff;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tcoordinates_t loc;\n\tif (locate_window(e->event, &loc)) {\n\t\t// prevent input focus stealing\n\t\tupdate_input_focus();\n\t}\n}\n\nvoid button_press(xcb_generic_event_t *evt)\n{\n\txcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;\n\tbool replay = false;\n\tfor (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {\n\t\tif (e->detail != BUTTONS[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tif ((click_to_focus == (int8_t) XCB_BUTTON_INDEX_ANY || click_to_focus == (int8_t) BUTTONS[i]) &&\n\t\t\tcleaned_mask(e->state) == XCB_NONE) {\n\t\t\tbool pff = pointer_follows_focus;\n\t\t\tbool pfm = pointer_follows_monitor;\n\t\t\tpointer_follows_focus = false;\n\t\t\tpointer_follows_monitor = false;\n\t\t\treplay = !grab_pointer(ACTION_FOCUS) || !swallow_first_click;\n\t\t\tpointer_follows_focus = pff;\n\t\t\tpointer_follows_monitor = pfm;\n\t\t} else {\n\t\t\tgrab_pointer(pointer_actions[i]);\n\t\t}\n\t}\n\txcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time);\n\txcb_flush(dpy);\n}\n\nvoid enter_notify(xcb_generic_event_t *evt)\n{\n\txcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;\n\txcb_window_t win = e->event;\n\n\tif (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {\n\t\treturn;\n\t}\n\n\t/* Ignore the enter notify events that we generated by unmapping the motion\n\t * recorder window in `query_pointer`. */\n\tif (motion_recorder.enabled && motion_recorder.sequence == e->sequence) {\n\t\treturn;\n\t}\n\n\tif (win == mon->root || (mon->desk->focus != NULL &&\n\t                         (win == mon->desk->focus->id ||\n\t                          (mon->desk->focus->presel != NULL &&\n\t                           win == mon->desk->focus->presel->feedback)))) {\n\t\treturn;\n\t}\n\n\tupdate_motion_recorder();\n}\n\nvoid motion_notify(xcb_generic_event_t *evt)\n{\n\txcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;\n\n\tstatic uint16_t last_motion_x = 0, last_motion_y = 0;\n\tstatic xcb_timestamp_t last_motion_time = 0;\n\n\tint64_t dtime = e->time - last_motion_time;\n\n\t/* Ignore unintentional pointer motions. */\n\tif (dtime > 1000) {\n\t\tlast_motion_time = e->time;\n\t\tlast_motion_x = e->event_x;\n\t\tlast_motion_y = e->event_y;\n\t\treturn;\n\t}\n\tint mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);\n\tif (mdist < 10) {\n\t\treturn;\n\t}\n\n\tdisable_motion_recorder();\n\n\txcb_window_t win = XCB_NONE;\n\tquery_pointer(&win, NULL);\n\tcoordinates_t loc;\n\tbool pff = pointer_follows_focus;\n\tbool pfm = pointer_follows_monitor;\n\tpointer_follows_focus = false;\n\tpointer_follows_monitor = false;\n\tauto_raise = false;\n\n\tif (locate_window(win, &loc)) {\n\t\tif (loc.monitor->desk == loc.desktop && loc.node != mon->desk->focus) {\n\t\t\tfocus_node(loc.monitor, loc.desktop, loc.node);\n\t\t}\n\t} else {\n\t\txcb_point_t pt = {e->root_x, e->root_y};\n\t\tmonitor_t *m = monitor_from_point(pt);\n\t\tif (m != NULL && m != mon) {\n\t\t\tfocus_node(m, m->desk, m->desk->focus);\n\t\t}\n\t}\n\n\tpointer_follows_focus = pff;\n\tpointer_follows_monitor = pfm;\n\tauto_raise = true;\n}\n\nvoid handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)\n{\n\tif (state == ewmh->_NET_WM_STATE_FULLSCREEN) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD && (ignore_ewmh_fullscreen & STATE_TRANSITION_ENTER) == 0) {\n\t\t\tset_state(m, d, n, STATE_FULLSCREEN);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE && (ignore_ewmh_fullscreen & STATE_TRANSITION_EXIT) == 0) {\n\t\t\tif (n->client->state == STATE_FULLSCREEN) {\n\t\t\t\tset_state(m, d, n, n->client->last_state);\n\t\t\t}\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tclient_state_t next_state = IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN;\n\t\t\tif ((next_state == STATE_FULLSCREEN && (ignore_ewmh_fullscreen & STATE_TRANSITION_ENTER) == 0) ||\n\t\t\t    (next_state != STATE_FULLSCREEN && (ignore_ewmh_fullscreen & STATE_TRANSITION_EXIT) == 0)) {\n\t\t\t\tset_state(m, d, n, next_state);\n\t\t\t}\n\t\t}\n\t\tarrange(m, d);\n\t} else if (state == ewmh->_NET_WM_STATE_BELOW) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) {\n\t\t\tset_layer(m, d, n, LAYER_BELOW);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) {\n\t\t\tif (n->client->layer == LAYER_BELOW) {\n\t\t\t\tset_layer(m, d, n, n->client->last_layer);\n\t\t\t}\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tset_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);\n\t\t}\n\t} else if (state == ewmh->_NET_WM_STATE_ABOVE) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) {\n\t\t\tset_layer(m, d, n, LAYER_ABOVE);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) {\n\t\t\tif (n->client->layer == LAYER_ABOVE) {\n\t\t\t\tset_layer(m, d, n, n->client->last_layer);\n\t\t\t}\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tset_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);\n\t\t}\n\t} else if (state == ewmh->_NET_WM_STATE_HIDDEN) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) {\n\t\t\tset_hidden(m, d, n, true);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) {\n\t\t\tset_hidden(m, d, n, false);\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tset_hidden(m, d, n, !n->hidden);\n\t\t}\n\t} else if (state == ewmh->_NET_WM_STATE_STICKY) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) {\n\t\t\tset_sticky(m, d, n, true);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) {\n\t\t\tset_sticky(m, d, n, false);\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tset_sticky(m, d, n, !n->sticky);\n\t\t}\n\t} else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) {\n\t\t\tset_urgent(m, d, n, true);\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) {\n\t\t\tset_urgent(m, d, n, false);\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) {\n\t\t\tset_urgent(m, d, n, !n->client->urgent);\n\t\t}\n#define HANDLE_WM_STATE(s)  \\\n\t} else if (state == ewmh->_NET_WM_STATE_##s) { \\\n\t\tif (action == XCB_EWMH_WM_STATE_ADD) { \\\n\t\t\tn->client->wm_flags |= WM_FLAG_##s; \\\n\t\t} else if (action == XCB_EWMH_WM_STATE_REMOVE) { \\\n\t\t\tn->client->wm_flags &= ~WM_FLAG_##s; \\\n\t\t} else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \\\n\t\t\tn->client->wm_flags ^= WM_FLAG_##s; \\\n\t\t} \\\n\t\tewmh_wm_state_update(n);\n\tHANDLE_WM_STATE(MODAL)\n\tHANDLE_WM_STATE(MAXIMIZED_VERT)\n\tHANDLE_WM_STATE(MAXIMIZED_HORZ)\n\tHANDLE_WM_STATE(SHADED)\n\tHANDLE_WM_STATE(SKIP_TASKBAR)\n\tHANDLE_WM_STATE(SKIP_PAGER)\n\t}\n#undef HANDLE_WM_STATE\n}\n\nvoid mapping_notify(xcb_generic_event_t *evt)\n{\n\tif (mapping_events_count == 0) {\n\t\treturn;\n\t}\n\n\txcb_mapping_notify_event_t *e = (xcb_mapping_notify_event_t *) evt;\n\n\tif (e->request == XCB_MAPPING_POINTER) {\n\t\treturn;\n\t}\n\n\tif (mapping_events_count > 0) {\n\t\tmapping_events_count--;\n\t}\n\n\tungrab_buttons();\n\tgrab_buttons();\n}\n\nvoid process_error(xcb_generic_event_t *evt)\n{\n\txcb_request_error_t *e = (xcb_request_error_t *) evt;\n\t/* Ignore unavoidable failed requests */\n\tif (e->error_code == ERROR_CODE_BAD_WINDOW) {\n\t\treturn;\n\t}\n\twarn(\"Failed request: %s, %s: 0x%08X.\\n\", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);\n}\n"
  },
  {
    "path": "src/events.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_EVENTS_H\n#define BSPWM_EVENTS_H\n\n#include <xcb/xcb.h>\n#include <xcb/xcb_event.h>\n\n#define ERROR_CODE_BAD_WINDOW  3\n\nextern uint8_t randr_base;\nstatic const xcb_button_index_t BUTTONS[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};\n\nvoid handle_event(xcb_generic_event_t *evt);\nvoid map_request(xcb_generic_event_t *evt);\nvoid configure_request(xcb_generic_event_t *evt);\nvoid configure_notify(xcb_generic_event_t *evt);\nvoid destroy_notify(xcb_generic_event_t *evt);\nvoid unmap_notify(xcb_generic_event_t *evt);\nvoid property_notify(xcb_generic_event_t *evt);\nvoid client_message(xcb_generic_event_t *evt);\nvoid focus_in(xcb_generic_event_t *evt);\nvoid button_press(xcb_generic_event_t *evt);\nvoid enter_notify(xcb_generic_event_t *evt);\nvoid motion_notify(xcb_generic_event_t *evt);\nvoid handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action);\nvoid mapping_notify(xcb_generic_event_t *evt);\nvoid process_error(xcb_generic_event_t *evt);\n\n#endif\n"
  },
  {
    "path": "src/ewmh.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdlib.h>\n#include <sys/types.h>\n#include <string.h>\n#include <unistd.h>\n#include \"bspwm.h\"\n#include \"settings.h\"\n#include \"tree.h\"\n#include \"ewmh.h\"\n\nxcb_ewmh_connection_t *ewmh;\n\nvoid ewmh_init(void)\n{\n\tewmh = calloc(1, sizeof(xcb_ewmh_connection_t));\n\tif (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {\n\t\terr(\"Can't initialize EWMH atoms.\\n\");\n\t}\n}\n\nvoid ewmh_update_active_window(void)\n{\n\txcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);\n\txcb_ewmh_set_active_window(ewmh, default_screen, win);\n}\n\nvoid ewmh_update_number_of_desktops(void)\n{\n\tuint32_t desktops_count = 0;\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tdesktops_count++;\n\t\t}\n\t}\n\n\txcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);\n}\n\nuint32_t ewmh_get_desktop_index(desktop_t *d)\n{\n\tuint32_t i = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {\n\t\t\tif (d == cd) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\nbool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {\n\t\t\tif (i == 0) {\n\t\t\t\tloc->monitor = m;\n\t\t\t\tloc->desktop = d;\n\t\t\t\tloc->node = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid ewmh_update_current_desktop(void)\n{\n\tif (mon == NULL) {\n\t\treturn;\n\t}\n\tuint32_t i = ewmh_get_desktop_index(mon->desk);\n\txcb_ewmh_set_current_desktop(ewmh, default_screen, i);\n}\n\nvoid ewmh_set_wm_desktop(node_t *n, desktop_t *d)\n{\n\tuint32_t i = ewmh_get_desktop_index(d);\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\txcb_ewmh_set_wm_desktop(ewmh, f->id, i);\n\t}\n}\n\nvoid ewmh_update_wm_desktops(void)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tuint32_t i = ewmh_get_desktop_index(d);\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\tif (n->client == NULL) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\txcb_ewmh_set_wm_desktop(ewmh, n->id, i);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid ewmh_update_desktop_names(void)\n{\n\tchar names[MAXLEN];\n\tunsigned int i, j;\n\tuint32_t names_len;\n\ti = 0;\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (j = 0; d->name[j] != '\\0' && (i + j) < sizeof(names); j++) {\n\t\t\t\tnames[i + j] = d->name[j];\n\t\t\t}\n\t\t\ti += j;\n\t\t\tif (i < sizeof(names)) {\n\t\t\t\tnames[i++] = '\\0';\n\t\t\t}\n\t\t}\n\t}\n\n\tif (i < 1) {\n\t\txcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);\n\t\treturn;\n\t}\n\n\tnames_len = i - 1;\n\txcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);\n}\n\nvoid ewmh_update_desktop_viewport(void)\n{\n\tuint32_t desktops_count = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tdesktops_count++;\n\t\t}\n\t}\n\tif (desktops_count == 0) {\n\t\txcb_ewmh_set_desktop_viewport(ewmh, default_screen, 0, NULL);\n\t\treturn;\n\t}\n\txcb_ewmh_coordinates_t coords[desktops_count];\n\tuint16_t desktop = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tcoords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};\n\t\t}\n\t}\n\txcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);\n}\n\nbool ewmh_handle_struts(xcb_window_t win)\n{\n\txcb_ewmh_wm_strut_partial_t struts;\n\tbool changed = false;\n\tif (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\txcb_rectangle_t rect = m->rectangle;\n\t\t\tif (rect.x < (int16_t) struts.left &&\n\t\t\t    (int16_t) struts.left < (rect.x + rect.width - 1) &&\n\t\t\t    (int16_t) struts.left_end_y >= rect.y &&\n\t\t\t    (int16_t) struts.left_start_y < (rect.y + rect.height)) {\n\t\t\t\tint dx = struts.left - rect.x;\n\t\t\t\tif (m->padding.left < 0) {\n\t\t\t\t\tm->padding.left += dx;\n\t\t\t\t} else {\n\t\t\t\t\tm->padding.left = MAX(dx, m->padding.left);\n\t\t\t\t}\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tif ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&\n\t\t\t    (int16_t) (screen_width - struts.right) > rect.x &&\n\t\t\t    (int16_t) struts.right_end_y >= rect.y &&\n\t\t\t    (int16_t) struts.right_start_y < (rect.y + rect.height)) {\n\t\t\t\tint dx = (rect.x + rect.width) - screen_width + struts.right;\n\t\t\t\tif (m->padding.right < 0) {\n\t\t\t\t\tm->padding.right += dx;\n\t\t\t\t} else {\n\t\t\t\t\tm->padding.right = MAX(dx, m->padding.right);\n\t\t\t\t}\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tif (rect.y < (int16_t) struts.top &&\n\t\t\t    (int16_t) struts.top < (rect.y + rect.height - 1) &&\n\t\t\t    (int16_t) struts.top_end_x >= rect.x &&\n\t\t\t    (int16_t) struts.top_start_x < (rect.x + rect.width)) {\n\t\t\t\tint dy = struts.top - rect.y;\n\t\t\t\tif (m->padding.top < 0) {\n\t\t\t\t\tm->padding.top += dy;\n\t\t\t\t} else {\n\t\t\t\t\tm->padding.top = MAX(dy, m->padding.top);\n\t\t\t\t}\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tif ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&\n\t\t\t    (int16_t) (screen_height - struts.bottom) > rect.y &&\n\t\t\t    (int16_t) struts.bottom_end_x >= rect.x &&\n\t\t\t    (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {\n\t\t\t\tint dy = (rect.y + rect.height) - screen_height + struts.bottom;\n\t\t\t\tif (m->padding.bottom < 0) {\n\t\t\t\t\tm->padding.bottom += dy;\n\t\t\t\t} else {\n\t\t\t\t\tm->padding.bottom = MAX(dy, m->padding.bottom);\n\t\t\t\t}\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t}\n\treturn changed;\n}\n\nvoid ewmh_update_client_list(bool stacking)\n{\n\tif (clients_count == 0) {\n\t\txcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);\n\t\txcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);\n\t\treturn;\n\t}\n\n\txcb_window_t wins[clients_count];\n\tunsigned int i = 0;\n\n\tif (stacking) {\n\t\tfor (stacking_list_t *s = stack_head; s != NULL; s = s->next) {\n\t\t\twins[i++] = s->node->id;\n\t\t}\n\t\txcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);\n\t} else {\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\t\tif (n->client == NULL) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\twins[i++] = n->id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\txcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);\n\t}\n}\n\nvoid ewmh_wm_state_update(node_t *n)\n{\n\tclient_t *c = n->client;\n\tsize_t count = 0;\n\tuint32_t values[12];\n#define HANDLE_WM_STATE(s)  \\\n\tif (WM_FLAG_##s & c->wm_flags) { \\\n\t\tvalues[count++] = ewmh->_NET_WM_STATE_##s; \\\n\t}\n\tHANDLE_WM_STATE(MODAL)\n\tHANDLE_WM_STATE(STICKY)\n\tHANDLE_WM_STATE(MAXIMIZED_VERT)\n\tHANDLE_WM_STATE(MAXIMIZED_HORZ)\n\tHANDLE_WM_STATE(SHADED)\n\tHANDLE_WM_STATE(SKIP_TASKBAR)\n\tHANDLE_WM_STATE(SKIP_PAGER)\n\tHANDLE_WM_STATE(HIDDEN)\n\tHANDLE_WM_STATE(FULLSCREEN)\n\tHANDLE_WM_STATE(ABOVE)\n\tHANDLE_WM_STATE(BELOW)\n\tHANDLE_WM_STATE(DEMANDS_ATTENTION)\n#undef HANDLE_WM_STATE\n\txcb_ewmh_set_wm_state(ewmh, n->id, count, values);\n}\n\nvoid ewmh_set_supporting(xcb_window_t win)\n{\n\tpid_t wm_pid = getpid();\n\txcb_ewmh_set_supporting_wm_check(ewmh, root, win);\n\txcb_ewmh_set_supporting_wm_check(ewmh, win, win);\n\txcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);\n\txcb_ewmh_set_wm_pid(ewmh, win, wm_pid);\n}\n"
  },
  {
    "path": "src/ewmh.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_EWMH_H\n#define BSPWM_EWMH_H\n\n#include <xcb/xcb_ewmh.h>\n\nextern xcb_ewmh_connection_t *ewmh;\n\nvoid ewmh_init(void);\nvoid ewmh_update_active_window(void);\nvoid ewmh_update_number_of_desktops(void);\nuint32_t ewmh_get_desktop_index(desktop_t *d);\nbool ewmh_locate_desktop(uint32_t i, coordinates_t *loc);\nvoid ewmh_update_current_desktop(void);\nvoid ewmh_set_wm_desktop(node_t *n, desktop_t *d);\nvoid ewmh_update_wm_desktops(void);\nvoid ewmh_update_desktop_names(void);\nvoid ewmh_update_desktop_viewport(void);\nbool ewmh_handle_struts(xcb_window_t win);\nvoid ewmh_update_client_list(bool stacking);\nvoid ewmh_wm_state_update(node_t *n);\nvoid ewmh_set_supporting(xcb_window_t win);\n\n#endif\n"
  },
  {
    "path": "src/geometry.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <math.h>\n#include \"types.h\"\n#include \"settings.h\"\n#include \"geometry.h\"\n\nbool is_inside(xcb_point_t p, xcb_rectangle_t r)\n{\n\treturn (p.x >= r.x && p.x < (r.x + r.width) &&\n\t        p.y >= r.y && p.y < (r.y + r.height));\n}\n\n/* Returns true if a contains b */\nbool contains(xcb_rectangle_t a, xcb_rectangle_t b)\n{\n\treturn (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&\n\t        a.y <= b.y && (a.y + a.height) >= (b.y + b.height));\n}\n\nunsigned int area(xcb_rectangle_t r)\n{\n\treturn r.width * r.height;\n}\n\n/* Distance between the `dir` edge of `r1` and the `opposite(dir)` edge of `r2`. */\nuint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)\n{\n\txcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1};\n\txcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1};\n\tswitch (dir) {\n\t\tcase DIR_NORTH:\n\t\t\treturn r2_max.y > r1.y ? r2_max.y - r1.y : r1.y - r2_max.y;\n\t\t\tbreak;\n\t\tcase DIR_WEST:\n\t\t\treturn r2_max.x > r1.x ? r2_max.x - r1.x : r1.x - r2_max.x;\n\t\t\tbreak;\n\t\tcase DIR_SOUTH:\n\t\t\treturn r2.y < r1_max.y ? r1_max.y - r2.y : r2.y - r1_max.y;\n\t\t\tbreak;\n\t\tcase DIR_EAST:\n\t\t\treturn r2.x < r1_max.x ? r1_max.x - r2.x : r2.x - r1_max.x;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn UINT32_MAX;\n\t}\n}\n\n/* Is `r2` on the `dir` side of `r1`? */\nbool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)\n{\n\txcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1};\n\txcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1};\n\n\t/* Eliminate rectangles on the opposite side */\n\tswitch (directional_focus_tightness) {\n\t\tcase TIGHTNESS_LOW:\n\t\t\tswitch (dir) {\n\t\t\t\tcase DIR_NORTH:\n\t\t\t\t\tif (r2.y > r1_max.y) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_WEST:\n\t\t\t\t\tif (r2.x > r1_max.x) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_SOUTH:\n\t\t\t\t\tif (r2_max.y < r1.y) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_EAST:\n\t\t\t\t\tif (r2_max.x < r1.x) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TIGHTNESS_HIGH:\n\t\t\tswitch (dir) {\n\t\t\t\tcase DIR_NORTH:\n\t\t\t\t\tif (r2.y >= r1.y) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_WEST:\n\t\t\t\t\tif (r2.x >= r1.x) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_SOUTH:\n\t\t\t\t\tif (r2_max.y <= r1_max.y) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_EAST:\n\t\t\t\t\tif (r2_max.x <= r1_max.x) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n\n\t/* Is there a shared vertical/horizontal range? */\n\tswitch (dir) {\n\t\tcase DIR_NORTH:\n\t\tcase DIR_SOUTH:\n\t\t\treturn\n\t\t\t\t(r2.x >= r1.x && r2.x <= r1_max.x) ||\n\t\t\t\t(r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||\n\t\t\t\t(r1.x > r2.x && r1.x < r2_max.x);\n\t\t\tbreak;\n\t\tcase DIR_WEST:\n\t\tcase DIR_EAST:\n\t\t\treturn\n\t\t\t\t(r2.y >= r1.y && r2.y <= r1_max.y) ||\n\t\t\t\t(r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||\n\t\t\t\t(r1.y > r2.y && r1_max.y < r2_max.y);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nbool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)\n{\n\treturn (a.x == b.x && a.y == b.y &&\n\t        a.width == b.width && a.height == b.height);\n}\n\nint rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)\n{\n\tif (r1.y >= (r2.y + r2.height)) {\n\t\treturn 1;\n\t} else if (r2.y >= (r1.y + r1.height)) {\n\t\treturn -1;\n\t} else {\n\t\tif (r1.x >= (r2.x + r2.width)) {\n\t\t\treturn 1;\n\t\t} else if (r2.x >= (r1.x + r1.width)) {\n\t\t\treturn -1;\n\t\t} else {\n\t\t\treturn area(r2) - area(r1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/geometry.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_GEOMETRY_H\n#define BSPWM_GEOMETRY_H\n\n#include <stdbool.h>\n#include <xcb/xcb.h>\n\nbool is_inside(xcb_point_t p, xcb_rectangle_t r);\nbool contains(xcb_rectangle_t a, xcb_rectangle_t b);\nunsigned int area(xcb_rectangle_t r);\nuint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);\nbool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);\nbool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);\nint rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);\n\n#endif\n"
  },
  {
    "path": "src/helpers.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <ctype.h>\n#include \"bspwm.h\"\n\nvoid warn(char *fmt, ...)\n{\n\tva_list ap;\n\tva_start(ap, fmt);\n\tvfprintf(stderr, fmt, ap);\n\tva_end(ap);\n}\n\n__attribute__((noreturn))\nvoid err(char *fmt, ...)\n{\n\tva_list ap;\n\tva_start(ap, fmt);\n\tvfprintf(stderr, fmt, ap);\n\tva_end(ap);\n\texit(EXIT_FAILURE);\n}\n\nchar *read_string(const char *file_path, size_t *tlen)\n{\n\tif (file_path == NULL) {\n\t\treturn NULL;\n\t}\n\n\tint fd = open(file_path, O_RDONLY);\n\n\tif (fd == -1) {\n\t\tperror(\"Read file: open\");\n\t\treturn NULL;\n\t}\n\n\tchar buf[BUFSIZ], *content = NULL;\n\tsize_t len = sizeof(buf);\n\n\tif ((content = calloc(len, sizeof(char))) == NULL) {\n\t\tperror(\"Read file: calloc\");\n\t\tgoto end;\n\t}\n\n\tint nb;\n\t*tlen = 0;\n\n\twhile (true) {\n\t\tnb = read(fd, buf, sizeof(buf));\n\t\tif (nb < 0) {\n\t\t\tperror(\"Restore tree: read\");\n\t\t\tfree(content);\n\t\t\tcontent = NULL;\n\t\t\tgoto end;\n\t\t} else if (nb == 0) {\n\t\t\tbreak;\n\t\t} else {\n\t\t\t*tlen += nb;\n\t\t\tif (*tlen > len) {\n\t\t\t\tlen *= 2;\n\t\t\t\tchar *rcontent = realloc(content, len * sizeof(char));\n\t\t\t\tif (rcontent == NULL) {\n\t\t\t\t\tperror(\"Read file: realloc\");\n\t\t\t\t\tfree(content);\n\t\t\t\t\tcontent = NULL;\n\t\t\t\t\tgoto end;\n\t\t\t\t} else {\n\t\t\t\t\tcontent = rcontent;\n\t\t\t\t}\n\t\t\t}\n\t\t\tstrncpy(content + (*tlen - nb), buf, nb);\n\t\t}\n\t}\n\nend:\n\tclose(fd);\n\treturn content;\n}\n\nchar *copy_string(char *str, size_t len)\n{\n\tchar *cpy = calloc(1, ((len+1) * sizeof(char)));\n\tif (cpy == NULL) {\n\t\tperror(\"Copy string: calloc\");\n\t\treturn NULL;\n\t}\n\tstrncpy(cpy, str, len);\n\tcpy[len] = '\\0';\n\treturn cpy;\n}\n\nchar *mktempfifo(const char *template)\n{\n\tint tempfd;\n\tchar *runtime_dir = getenv(RUNTIME_DIR_ENV);\n\tif (runtime_dir == NULL) {\n\t\truntime_dir = \"/tmp\";\n\t}\n\n\tchar *fifo_path = malloc(strlen(runtime_dir)+1+strlen(template)+1);\n\tif (fifo_path == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsprintf(fifo_path, \"%s/%s\", runtime_dir, template);\n\n\tif ((tempfd = mkstemp(fifo_path)) == -1) {\n\t\tfree(fifo_path);\n\t\treturn NULL;\n\t}\n\n\tclose(tempfd);\n\tunlink(fifo_path);\n\n\tif (mkfifo(fifo_path, 0666) == -1) {\n\t\tfree(fifo_path);\n\t\treturn NULL;\n\t}\n\n\treturn fifo_path;\n}\n\nint asprintf(char **buf, const char *fmt, ...)\n{\n\tva_list args;\n\tva_start(args, fmt);\n\tint size = vasprintf(buf, fmt, args);\n\tva_end(args);\n\treturn size;\n}\n\nint vasprintf(char **buf, const char *fmt, va_list args)\n{\n\tva_list tmp;\n\tva_copy(tmp, args);\n\tint size = vsnprintf(NULL, 0, fmt, tmp);\n\tva_end(tmp);\n\n\tif (size < 0) {\n\t\treturn -1;\n\t}\n\n\t*buf = malloc(size + 1);\n\n\tif (*buf == NULL) {\n\t\treturn -1;\n\t}\n\n\tsize = vsprintf(*buf, fmt, args);\n\treturn size;\n}\n\nbool is_hex_color(const char *color)\n{\n\tif (color[0] != '#' || strlen(color) != 7) {\n\t\treturn false;\n\t}\n\tfor (int i = 1; i < 7; i++) {\n\t\tif (!isxdigit(color[i])) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nchar *tokenize_with_escape(struct tokenize_state *state, const char *s, char sep)\n{\n\tif (s != NULL) {\n\t\t// first call\n\t\tstate->in_escape = false;\n\t\tstate->pos = s;\n\t\tstate->len = strlen(s) + 1;\n\t}\n\n\tchar *outp = calloc(state->len, 1);\n\tchar *ret = outp;\n\tif (!outp) return NULL;\n\n\tchar cur;\n\twhile (*state->pos) {\n\t\t--state->len;\n\t\tcur = *state->pos++;\n\n\t\tif (state->in_escape) {\n\t\t\t*outp++ = cur;\n\t\t\tstate->in_escape = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (cur == '\\\\') {\n\t\t\tstate->in_escape = !state->in_escape;\n\t\t} else if (cur == sep) {\n\t\t\treturn ret;\n\t\t} else {\n\t\t\t*outp++ = cur;\n\t\t}\n\t}\n\n\treturn ret;\n}\n"
  },
  {
    "path": "src/helpers.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_HELPERS_H\n#define BSPWM_HELPERS_H\n\n#include <xcb/xcb.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <float.h>\n\n#define LENGTH(x)         (sizeof(x) / sizeof(*x))\n#define MAX(A, B)         ((A) > (B) ? (A) : (B))\n#define MIN(A, B)         ((A) < (B) ? (A) : (B))\n\n#define IS_TILED(c)       (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)\n#define IS_FLOATING(c)    (c->state == STATE_FLOATING)\n#define IS_FULLSCREEN(c)  (c->state == STATE_FULLSCREEN)\n#define IS_RECEPTACLE(n)  (is_leaf(n) && n->client == NULL)\n\n#define BOOL_STR(A)       ((A) ? \"true\" : \"false\")\n#define ON_OFF_STR(A)     ((A) ? \"on\" : \"off\")\n#define LAYOUT_STR(A)     ((A) == LAYOUT_TILED ? \"tiled\" : \"monocle\")\n#define LAYOUT_CHR(A)     ((A) == LAYOUT_TILED ? 'T' : 'M')\n#define CHILD_POL_STR(A)  ((A) == FIRST_CHILD ? \"first_child\" : \"second_child\")\n#define AUTO_SCM_STR(A)   ((A) == SCHEME_LONGEST_SIDE ? \"longest_side\" : ((A) == SCHEME_ALTERNATE ? \"alternate\" : \"spiral\"))\n#define TIGHTNESS_STR(A)  ((A) == TIGHTNESS_HIGH ? \"high\" : \"low\")\n#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? \"horizontal\" : \"vertical\")\n#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? \"automatic\" : \"manual\")\n#define SPLIT_DIR_STR(A)  ((A) == DIR_NORTH ? \"north\" : ((A) == DIR_WEST ? \"west\" : ((A) == DIR_SOUTH ? \"south\" : \"east\")))\n#define STATE_STR(A)      ((A) == STATE_TILED ? \"tiled\" : ((A) == STATE_FLOATING ? \"floating\" : ((A) == STATE_FULLSCREEN ? \"fullscreen\" : \"pseudo_tiled\")))\n#define STATE_CHR(A)      ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))\n#define LAYER_STR(A)      ((A) == LAYER_BELOW ? \"below\" : ((A) == LAYER_NORMAL ? \"normal\" : \"above\"))\n#define HSH_MODE_STR(A)   ((A) == HONOR_SIZE_HINTS_TILED ? \"tiled\" : ((A) == HONOR_SIZE_HINTS_FLOATING ? \"floating\" : BOOL_STR(A)))\n\n#define XCB_CONFIG_WINDOW_X_Y               (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)\n#define XCB_CONFIG_WINDOW_WIDTH_HEIGHT      (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)\n#define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT  (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)\n\n#define SHOULD_HONOR_SIZE_HINTS(hsh, state) \\\n\t((hsh) && \\\n\t (((hsh) == HONOR_SIZE_HINTS_YES && \\\n\t   (state) != STATE_FULLSCREEN) || \\\n\t  ((hsh) == HONOR_SIZE_HINTS_TILED && \\\n\t   (state) == STATE_TILED) || \\\n\t  ((hsh) == HONOR_SIZE_HINTS_FLOATING && \\\n\t   ((state) == STATE_FLOATING || (state) == STATE_PSEUDO_TILED))))\n\n#define MAXLEN    256\n#define SMALEN     32\n#define INIT_CAP    8\n\n#define cleaned_mask(m)   (m & ~(num_lock | scroll_lock | caps_lock))\n#define streq(s1, s2)     (strcmp((s1), (s2)) == 0)\n#define unsigned_subtract(a, b)  \\\n\tdo {                         \\\n\t\tif (b > a) {             \\\n\t\t\ta = 0;               \\\n\t\t} else {                 \\\n\t\t\ta -= b;              \\\n\t\t}                        \\\n\t} while (false)\n\n\nvoid warn(char *fmt, ...);\nvoid err(char *fmt, ...);\nchar *read_string(const char *file_path, size_t *tlen);\nchar *copy_string(char *str, size_t len);\nchar *mktempfifo(const char *template);\nint asprintf(char **buf, const char *fmt, ...);\nint vasprintf(char **buf, const char *fmt, va_list args);\nbool is_hex_color(const char *color);\n\nstruct tokenize_state {\n\tbool in_escape;\n\tconst char *pos;\n\tsize_t len;\n};\nchar *tokenize_with_escape(struct tokenize_state *state, const char *s, char sep);\n\n#endif\n"
  },
  {
    "path": "src/history.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdlib.h>\n#include <stdbool.h>\n#include \"bspwm.h\"\n#include \"tree.h\"\n#include \"query.h\"\n#include \"history.h\"\n\nhistory_t *make_history(monitor_t *m, desktop_t *d, node_t *n)\n{\n\thistory_t *h = calloc(1, sizeof(history_t));\n\th->loc = (coordinates_t) {m, d, n};\n\th->prev = h->next = NULL;\n\th->latest = true;\n\treturn h;\n}\n\nvoid history_add(monitor_t *m, desktop_t *d, node_t *n, bool focused)\n{\n\tif (!record_history) {\n\t\treturn;\n\t}\n\n\tif (focused) {\n\t\thistory_needle = NULL;\n\t}\n\n\thistory_t *h = make_history(m, d, n);\n\n\tif (history_head == NULL) {\n\t\thistory_head = history_tail = h;\n\t} else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {\n\t\thistory_t *ip = focused ? history_tail : NULL;\n\n\t\tfor (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {\n\t\t\tif ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {\n\t\t\t\thh->latest = false;\n\t\t\t}\n\t\t\tif (ip == NULL && ((n != NULL && hh->loc.desktop == d) || (n == NULL && hh->loc.monitor == m))) {\n\t\t\t\tip = hh;\n\t\t\t}\n\t\t}\n\n\t\tif (ip != NULL) {\n\t\t\thistory_insert_after(h, ip);\n\t\t} else {\n\t\t\tip = history_head;\n\t\t\tif (n != NULL) {\n\t\t\t\tfor (history_t *hh = history_head; hh != NULL; hh = hh->next) {\n\t\t\t\t\tif (hh->latest && hh->loc.monitor == m) {\n\t\t\t\t\t\tip = hh;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\thistory_insert_before(h, ip);\n\t\t}\n\t} else {\n\t\tfree(h);\n\t}\n}\n\n// Inserts `a` after `b`.\nvoid history_insert_after(history_t *a, history_t *b)\n{\n\ta->next = b->next;\n\tif (b->next != NULL) {\n\t\tb->next->prev = a;\n\t}\n\tb->next = a;\n\ta->prev = b;\n\tif (history_tail == b) {\n\t\thistory_tail = a;\n\t}\n}\n\n// Inserts `a` before `b`.\nvoid history_insert_before(history_t *a, history_t *b)\n{\n\ta->prev = b->prev;\n\tif (b->prev != NULL) {\n\t\tb->prev->next = a;\n\t}\n\tb->prev = a;\n\ta->next = b;\n\tif (history_head == b) {\n\t\thistory_head = a;\n\t}\n}\n\nvoid history_remove(desktop_t *d, node_t *n, bool deep)\n{\n\t/* removing from the newest to the oldest is required */\n\t/* for maintaining the *latest* attribute */\n\thistory_t *b = history_tail;\n\twhile (b != NULL) {\n\t\tif ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||\n\t\t    (n == NULL && d == b->loc.desktop)) {\n\t\t\thistory_t *a = b->next;\n\t\t\thistory_t *c = b->prev;\n\t\t\tif (a != NULL) {\n\t\t\t\t/* remove duplicate entries */\n\t\t\t\twhile (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||\n\t\t\t\t       (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {\n\t\t\t\t\thistory_t *p = c->prev;\n\t\t\t\t\tif (history_head == c) {\n\t\t\t\t\t\thistory_head = history_tail;\n\t\t\t\t\t}\n\t\t\t\t\tif (history_needle == c) {\n\t\t\t\t\t\thistory_needle = history_tail;\n\t\t\t\t\t}\n\t\t\t\t\tfree(c);\n\t\t\t\t\tc = p;\n\t\t\t\t}\n\t\t\t\ta->prev = c;\n\t\t\t}\n\t\t\tif (c != NULL) {\n\t\t\t\tc->next = a;\n\t\t\t}\n\t\t\tif (history_tail == b) {\n\t\t\t\thistory_tail = c;\n\t\t\t}\n\t\t\tif (history_head == b) {\n\t\t\t\thistory_head = a;\n\t\t\t}\n\t\t\tif (history_needle == b) {\n\t\t\t\thistory_needle = c;\n\t\t\t}\n\t\t\tfree(b);\n\t\t\tb = c;\n\t\t} else {\n\t\t\tb = b->prev;\n\t\t}\n\t}\n}\n\nvoid empty_history(void)\n{\n\thistory_t *h = history_head;\n\twhile (h != NULL) {\n\t\thistory_t *next = h->next;\n\t\tfree(h);\n\t\th = next;\n\t}\n\thistory_head = history_tail = NULL;\n}\n\nnode_t *history_last_node(desktop_t *d, node_t *n)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (h->latest && h->loc.node != NULL && !h->loc.node->hidden &&\n\t\t    !is_descendant(h->loc.node, n) && h->loc.desktop == d) {\n\t\t\treturn h->loc.node;\n\t\t}\n\t}\n\treturn NULL;\n}\n\ndesktop_t *history_last_desktop(monitor_t *m, desktop_t *d)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (h->latest && h->loc.desktop != d && h->loc.monitor == m) {\n\t\t\treturn h->loc.desktop;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nmonitor_t *history_last_monitor(monitor_t *m)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (h->latest && h->loc.monitor != m) {\n\t\t\treturn h->loc.monitor;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nbool history_find_newest_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (h->loc.node == NULL ||\n\t\t    h->loc.node->hidden ||\n\t\t    !node_matches(&h->loc, ref, sel)) {\n\t\t\tcontinue;\n\t\t}\n\t\t*dst = h->loc;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tif (history_needle == NULL || record_history) {\n\t\thistory_needle = history_tail;\n\t}\n\n\thistory_t *h;\n\tfor (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {\n\t\tif (!h->latest ||\n\t\t    h->loc.node == NULL ||\n\t\t    h->loc.node == ref->node ||\n\t\t    h->loc.node->hidden ||\n\t\t    !node_matches(&h->loc, ref, sel)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!record_history) {\n\t\t\thistory_needle = h;\n\t\t}\n\t\t*dst = h->loc;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool history_find_newest_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (desktop_matches(&h->loc, ref, sel)) {\n\t\t\t*dst = h->loc;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel)\n{\n\tif (history_needle == NULL || record_history) {\n\t\thistory_needle = history_tail;\n\t}\n\n\thistory_t *h;\n\tfor (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {\n\t\tif (!h->latest ||\n\t\t    h->loc.desktop == ref->desktop ||\n\t\t    !desktop_matches(&h->loc, ref, sel)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!record_history) {\n\t\t\thistory_needle = h;\n\t\t}\n\t\t*dst = h->loc;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool history_find_newest_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel)\n{\n\tfor (history_t *h = history_tail; h != NULL; h = h->prev) {\n\t\tif (monitor_matches(&h->loc, ref, sel)) {\n\t\t\t*dst = h->loc;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel)\n{\n\tif (history_needle == NULL || record_history) {\n\t\thistory_needle = history_tail;\n\t}\n\n\thistory_t *h;\n\n\tfor (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {\n\t\tif (!h->latest ||\n\t\t    h->loc.monitor == ref->monitor ||\n\t\t    !monitor_matches(&h->loc, ref, sel)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!record_history) {\n\t\t\thistory_needle = h;\n\t\t}\n\t\t*dst = h->loc;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nuint32_t history_rank(node_t *n)\n{\n\tuint32_t r = 0;\n\thistory_t *h = history_tail;\n\twhile (h != NULL && (!h->latest || h->loc.node != n)) {\n\t\th = h->prev;\n\t\tr++;\n\t}\n\tif (h == NULL) {\n\t\treturn UINT32_MAX;\n\t} else {\n\t\treturn r;\n\t}\n}\n"
  },
  {
    "path": "src/history.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_HISTORY_H\n#define BSPWM_HISTORY_H\n\n#include \"types.h\"\n\nhistory_t *make_history(monitor_t *m, desktop_t *d, node_t *n);\nvoid history_add(monitor_t *m, desktop_t *d, node_t *n, bool focused);\nvoid history_insert_after(history_t *a, history_t *b);\nvoid history_insert_before(history_t *a, history_t *b);\nvoid history_remove(desktop_t *d, node_t *n, bool deep);\nvoid empty_history(void);\nnode_t *history_last_node(desktop_t *d, node_t *n);\ndesktop_t *history_last_desktop(monitor_t *m, desktop_t *d);\nmonitor_t *history_last_monitor(monitor_t *m);\nbool history_find_newest_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nbool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nbool history_find_newest_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel);\nbool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel);\nbool history_find_newest_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel);\nbool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel);\nuint32_t history_rank(node_t *n);\n\n#endif\n"
  },
  {
    "path": "src/jsmn.c",
    "content": "#include \"jsmn.h\"\n\n/**\n * Allocates a fresh unused token from the token pool.\n */\nstatic jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,\n\t\tjsmntok_t *tokens, size_t num_tokens) {\n\tjsmntok_t *tok;\n\tif (parser->toknext >= num_tokens) {\n\t\treturn NULL;\n\t}\n\ttok = &tokens[parser->toknext++];\n\ttok->start = tok->end = -1;\n\ttok->size = 0;\n#ifdef JSMN_PARENT_LINKS\n\ttok->parent = -1;\n#endif\n\treturn tok;\n}\n\n/**\n * Fills token type and boundaries.\n */\nstatic void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,\n                            int start, int end) {\n\ttoken->type = type;\n\ttoken->start = start;\n\ttoken->end = end;\n\ttoken->size = 0;\n}\n\n/**\n * Fills next available token with JSON primitive.\n */\nstatic int jsmn_parse_primitive(jsmn_parser *parser, const char *js,\n\t\tsize_t len, jsmntok_t *tokens, size_t num_tokens) {\n\tjsmntok_t *token;\n\tint start;\n\n\tstart = parser->pos;\n\n\tfor (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n\t\tswitch (js[parser->pos]) {\n#ifndef JSMN_STRICT\n\t\t\t/* In strict mode primitive must be followed by \",\" or \"}\" or \"]\" */\n\t\t\tcase ':':\n#endif\n\t\t\tcase '\\t' : case '\\r' : case '\\n' : case ' ' :\n\t\t\tcase ','  : case ']'  : case '}' :\n\t\t\t\tgoto found;\n\t\t}\n\t\tif (js[parser->pos] < 32 || js[parser->pos] >= 127) {\n\t\t\tparser->pos = start;\n\t\t\treturn JSMN_ERROR_INVAL;\n\t\t}\n\t}\n#ifdef JSMN_STRICT\n\t/* In strict mode primitive must be followed by a comma/object/array */\n\tparser->pos = start;\n\treturn JSMN_ERROR_PART;\n#endif\n\nfound:\n\tif (tokens == NULL) {\n\t\tparser->pos--;\n\t\treturn 0;\n\t}\n\ttoken = jsmn_alloc_token(parser, tokens, num_tokens);\n\tif (token == NULL) {\n\t\tparser->pos = start;\n\t\treturn JSMN_ERROR_NOMEM;\n\t}\n\tjsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);\n#ifdef JSMN_PARENT_LINKS\n\ttoken->parent = parser->toksuper;\n#endif\n\tparser->pos--;\n\treturn 0;\n}\n\n/**\n * Fills next token with JSON string.\n */\nstatic int jsmn_parse_string(jsmn_parser *parser, const char *js,\n\t\tsize_t len, jsmntok_t *tokens, size_t num_tokens) {\n\tjsmntok_t *token;\n\n\tint start = parser->pos;\n\n\tparser->pos++;\n\n\t/* Skip starting quote */\n\tfor (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n\t\tchar c = js[parser->pos];\n\n\t\t/* Quote: end of string */\n\t\tif (c == '\\\"') {\n\t\t\tif (tokens == NULL) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\ttoken = jsmn_alloc_token(parser, tokens, num_tokens);\n\t\t\tif (token == NULL) {\n\t\t\t\tparser->pos = start;\n\t\t\t\treturn JSMN_ERROR_NOMEM;\n\t\t\t}\n\t\t\tjsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);\n#ifdef JSMN_PARENT_LINKS\n\t\t\ttoken->parent = parser->toksuper;\n#endif\n\t\t\treturn 0;\n\t\t}\n\n\t\t/* Backslash: Quoted symbol expected */\n\t\tif (c == '\\\\' && parser->pos + 1 < len) {\n\t\t\tint i;\n\t\t\tparser->pos++;\n\t\t\tswitch (js[parser->pos]) {\n\t\t\t\t/* Allowed escaped symbols */\n\t\t\t\tcase '\\\"': case '/' : case '\\\\' : case 'b' :\n\t\t\t\tcase 'f' : case 'r' : case 'n'  : case 't' :\n\t\t\t\t\tbreak;\n\t\t\t\t/* Allows escaped symbol \\uXXXX */\n\t\t\t\tcase 'u':\n\t\t\t\t\tparser->pos++;\n\t\t\t\t\tfor(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\\0'; i++) {\n\t\t\t\t\t\t/* If it isn't a hex character we have an error */\n\t\t\t\t\t\tif(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */\n\t\t\t\t\t\t\t\t\t(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */\n\t\t\t\t\t\t\t\t\t(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */\n\t\t\t\t\t\t\tparser->pos = start;\n\t\t\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparser->pos++;\n\t\t\t\t\t}\n\t\t\t\t\tparser->pos--;\n\t\t\t\t\tbreak;\n\t\t\t\t/* Unexpected symbol */\n\t\t\t\tdefault:\n\t\t\t\t\tparser->pos = start;\n\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t}\n\t\t}\n\t}\n\tparser->pos = start;\n\treturn JSMN_ERROR_PART;\n}\n\n/**\n * Parse JSON string and fill tokens.\n */\nint jsmn_parse(jsmn_parser *parser, const char *js, size_t len,\n\t\tjsmntok_t *tokens, unsigned int num_tokens) {\n\tint r;\n\tint i;\n\tjsmntok_t *token;\n\tint count = parser->toknext;\n\n\tfor (; parser->pos < len && js[parser->pos] != '\\0'; parser->pos++) {\n\t\tchar c;\n\t\tjsmntype_t type;\n\n\t\tc = js[parser->pos];\n\t\tswitch (c) {\n\t\t\tcase '{': case '[':\n\t\t\t\tcount++;\n\t\t\t\tif (tokens == NULL) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ttoken = jsmn_alloc_token(parser, tokens, num_tokens);\n\t\t\t\tif (token == NULL)\n\t\t\t\t\treturn JSMN_ERROR_NOMEM;\n\t\t\t\tif (parser->toksuper != -1) {\n\t\t\t\t\ttokens[parser->toksuper].size++;\n#ifdef JSMN_PARENT_LINKS\n\t\t\t\t\ttoken->parent = parser->toksuper;\n#endif\n\t\t\t\t}\n\t\t\t\ttoken->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);\n\t\t\t\ttoken->start = parser->pos;\n\t\t\t\tparser->toksuper = parser->toknext - 1;\n\t\t\t\tbreak;\n\t\t\tcase '}': case ']':\n\t\t\t\tif (tokens == NULL)\n\t\t\t\t\tbreak;\n\t\t\t\ttype = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);\n#ifdef JSMN_PARENT_LINKS\n\t\t\t\tif (parser->toknext < 1) {\n\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t}\n\t\t\t\ttoken = &tokens[parser->toknext - 1];\n\t\t\t\tfor (;;) {\n\t\t\t\t\tif (token->start != -1 && token->end == -1) {\n\t\t\t\t\t\tif (token->type != type) {\n\t\t\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttoken->end = parser->pos + 1;\n\t\t\t\t\t\tparser->toksuper = token->parent;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (token->parent == -1) {\n\t\t\t\t\t\tif(token->type != type || parser->toksuper == -1) {\n\t\t\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\ttoken = &tokens[token->parent];\n\t\t\t\t}\n#else\n\t\t\t\tfor (i = parser->toknext - 1; i >= 0; i--) {\n\t\t\t\t\ttoken = &tokens[i];\n\t\t\t\t\tif (token->start != -1 && token->end == -1) {\n\t\t\t\t\t\tif (token->type != type) {\n\t\t\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparser->toksuper = -1;\n\t\t\t\t\t\ttoken->end = parser->pos + 1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t/* Error if unmatched closing bracket */\n\t\t\t\tif (i == -1) return JSMN_ERROR_INVAL;\n\t\t\t\tfor (; i >= 0; i--) {\n\t\t\t\t\ttoken = &tokens[i];\n\t\t\t\t\tif (token->start != -1 && token->end == -1) {\n\t\t\t\t\t\tparser->toksuper = i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tr = jsmn_parse_string(parser, js, len, tokens, num_tokens);\n\t\t\t\tif (r < 0) return r;\n\t\t\t\tcount++;\n\t\t\t\tif (parser->toksuper != -1 && tokens != NULL)\n\t\t\t\t\ttokens[parser->toksuper].size++;\n\t\t\t\tbreak;\n\t\t\tcase '\\t' : case '\\r' : case '\\n' : case ' ':\n\t\t\t\tbreak;\n\t\t\tcase ':':\n\t\t\t\tparser->toksuper = parser->toknext - 1;\n\t\t\t\tbreak;\n\t\t\tcase ',':\n\t\t\t\tif (tokens != NULL && parser->toksuper != -1 &&\n\t\t\t\t\t\ttokens[parser->toksuper].type != JSMN_ARRAY &&\n\t\t\t\t\t\ttokens[parser->toksuper].type != JSMN_OBJECT) {\n#ifdef JSMN_PARENT_LINKS\n\t\t\t\t\tparser->toksuper = tokens[parser->toksuper].parent;\n#else\n\t\t\t\t\tfor (i = parser->toknext - 1; i >= 0; i--) {\n\t\t\t\t\t\tif (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {\n\t\t\t\t\t\t\tif (tokens[i].start != -1 && tokens[i].end == -1) {\n\t\t\t\t\t\t\t\tparser->toksuper = i;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t\tbreak;\n#ifdef JSMN_STRICT\n\t\t\t/* In strict mode primitives are: numbers and booleans */\n\t\t\tcase '-': case '0': case '1' : case '2': case '3' : case '4':\n\t\t\tcase '5': case '6': case '7' : case '8': case '9':\n\t\t\tcase 't': case 'f': case 'n' :\n\t\t\t\t/* And they must not be keys of the object */\n\t\t\t\tif (tokens != NULL && parser->toksuper != -1) {\n\t\t\t\t\tjsmntok_t *t = &tokens[parser->toksuper];\n\t\t\t\t\tif (t->type == JSMN_OBJECT ||\n\t\t\t\t\t\t\t(t->type == JSMN_STRING && t->size != 0)) {\n\t\t\t\t\t\treturn JSMN_ERROR_INVAL;\n\t\t\t\t\t}\n\t\t\t\t}\n#else\n\t\t\t/* In non-strict mode every unquoted value is a primitive */\n\t\t\tdefault:\n#endif\n\t\t\t\tr = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);\n\t\t\t\tif (r < 0) return r;\n\t\t\t\tcount++;\n\t\t\t\tif (parser->toksuper != -1 && tokens != NULL)\n\t\t\t\t\ttokens[parser->toksuper].size++;\n\t\t\t\tbreak;\n\n#ifdef JSMN_STRICT\n\t\t\t/* Unexpected char in strict mode */\n\t\t\tdefault:\n\t\t\t\treturn JSMN_ERROR_INVAL;\n#endif\n\t\t}\n\t}\n\n\tif (tokens != NULL) {\n\t\tfor (i = parser->toknext - 1; i >= 0; i--) {\n\t\t\t/* Unmatched opened object or array */\n\t\t\tif (tokens[i].start != -1 && tokens[i].end == -1) {\n\t\t\t\treturn JSMN_ERROR_PART;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn count;\n}\n\n/**\n * Creates a new parser based over a given  buffer with an array of tokens\n * available.\n */\nvoid jsmn_init(jsmn_parser *parser) {\n\tparser->pos = 0;\n\tparser->toknext = 0;\n\tparser->toksuper = -1;\n}\n\n"
  },
  {
    "path": "src/jsmn.h",
    "content": "#ifndef __JSMN_H_\n#define __JSMN_H_\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * JSON type identifier. Basic types are:\n * \to Object\n * \to Array\n * \to String\n * \to Other primitive: number, boolean (true/false) or null\n */\ntypedef enum {\n\tJSMN_UNDEFINED = 0,\n\tJSMN_OBJECT = 1,\n\tJSMN_ARRAY = 2,\n\tJSMN_STRING = 3,\n\tJSMN_PRIMITIVE = 4\n} jsmntype_t;\n\nenum jsmnerr {\n\t/* Not enough tokens were provided */\n\tJSMN_ERROR_NOMEM = -1,\n\t/* Invalid character inside JSON string */\n\tJSMN_ERROR_INVAL = -2,\n\t/* The string is not a full JSON packet, more bytes expected */\n\tJSMN_ERROR_PART = -3\n};\n\n/**\n * JSON token description.\n * type\t\ttype (object, array, string etc.)\n * start\tstart position in JSON data string\n * end\t\tend position in JSON data string\n */\ntypedef struct {\n\tjsmntype_t type;\n\tint start;\n\tint end;\n\tint size;\n#ifdef JSMN_PARENT_LINKS\n\tint parent;\n#endif\n} jsmntok_t;\n\n/**\n * JSON parser. Contains an array of token blocks available. Also stores\n * the string being parsed now and current position in that string\n */\ntypedef struct {\n\tunsigned int pos; /* offset in the JSON string */\n\tunsigned int toknext; /* next token to allocate */\n\tint toksuper; /* superior token node, e.g parent object or array */\n} jsmn_parser;\n\n/**\n * Create JSON parser over an array of tokens\n */\nvoid jsmn_init(jsmn_parser *parser);\n\n/**\n * Run JSON parser. It parses a JSON data string into and array of tokens, each describing\n * a single JSON object.\n */\nint jsmn_parse(jsmn_parser *parser, const char *js, size_t len,\n\t\tjsmntok_t *tokens, unsigned int num_tokens);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __JSMN_H_ */\n"
  },
  {
    "path": "src/messages.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n#include <stdarg.h>\n#include <inttypes.h>\n#include <unistd.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"monitor.h\"\n#include \"pointer.h\"\n#include \"query.h\"\n#include \"rule.h\"\n#include \"restore.h\"\n#include \"settings.h\"\n#include \"tree.h\"\n#include \"window.h\"\n#include \"common.h\"\n#include \"parse.h\"\n#include \"messages.h\"\n\nvoid handle_message(char *msg, int msg_len, FILE *rsp)\n{\n\tint cap = INIT_CAP;\n\tint num = 0;\n\tchar **args = calloc(cap, sizeof(char *));\n\n\tif (args == NULL) {\n\t\tperror(\"Handle message: calloc\");\n\t\treturn;\n\t}\n\n\tfor (int i = 0, j = 0; i < msg_len; i++) {\n\t\tif (msg[i] == 0) {\n\t\t\targs[num++] = msg + j;\n\t\t\tj = i + 1;\n\t\t}\n\t\tif (num >= cap) {\n\t\t\tcap *= 2;\n\t\t\tchar **new = realloc(args, cap * sizeof(char *));\n\t\t\tif (new == NULL) {\n\t\t\t\tfree(args);\n\t\t\t\tperror(\"Handle message: realloc\");\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\targs = new;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (num < 1) {\n\t\tfree(args);\n\t\tfail(rsp, \"No arguments given.\\n\");\n\t\treturn;\n\t}\n\n\tchar **args_orig = args;\n\tprocess_message(args, num, rsp);\n\tfree(args_orig);\n}\n\nvoid process_message(char **args, int num, FILE *rsp)\n{\n\tif (streq(\"node\", *args)) {\n\t\tcmd_node(++args, --num, rsp);\n\t} else if (streq(\"desktop\", *args)) {\n\t\tcmd_desktop(++args, --num, rsp);\n\t} else if (streq(\"monitor\", *args)) {\n\t\tcmd_monitor(++args, --num, rsp);\n\t} else if (streq(\"query\", *args)) {\n\t\tcmd_query(++args, --num, rsp);\n\t} else if (streq(\"subscribe\", *args)) {\n\t\tcmd_subscribe(++args, --num, rsp);\n\t\treturn;\n\t} else if (streq(\"wm\", *args)) {\n\t\tcmd_wm(++args, --num, rsp);\n\t} else if (streq(\"rule\", *args)) {\n\t\tcmd_rule(++args, --num, rsp);\n\t} else if (streq(\"config\", *args)) {\n\t\tcmd_config(++args, --num, rsp);\n\t} else if (streq(\"quit\", *args)) {\n\t\tcmd_quit(++args, --num, rsp);\n\t} else {\n\t\tfail(rsp, \"Unknown domain or command: '%s'.\\n\", *args);\n\t}\n\n\tfflush(rsp);\n\tfclose(rsp);\n}\n\nvoid cmd_node(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"node: Missing arguments.\\n\");\n\t\treturn;\n\t}\n\n\tcoordinates_t ref = {mon, mon->desk, mon->desk->focus};\n\tcoordinates_t trg = ref;\n\n\tif ((*args)[0] != OPT_CHR) {\n\t\tint ret;\n\t\tif ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {\n\t\t\tnum--, args++;\n\t\t} else {\n\t\t\thandle_failure(ret, \"node\", *args, rsp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (num < 1) {\n\t\tfail(rsp, \"node: Missing commands.\\n\");\n\t\treturn;\n\t}\n\n\tbool changed = false;\n\n\twhile (num > 0) {\n\t\tif (streq(\"-f\", *args) || streq(\"--focus\", *args)) {\n\t\t\tcoordinates_t dst = trg;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"node -f\", *args, rsp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-a\", *args) || streq(\"--activate\", *args)) {\n\t\t\tcoordinates_t dst = trg;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"node -a\", *args, rsp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-d\", *args) || streq(\"--to-desktop\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t\ttrg.desktop = dst.desktop;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"node -d\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-m\", *args) || streq(\"--to-monitor\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t\ttrg.desktop = dst.monitor->desk;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"node -m\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-n\", *args) || streq(\"--to-node\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t\ttrg.desktop = dst.desktop;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"node -n\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-s\", *args) || streq(\"--swap\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t\ttrg.desktop = dst.desktop;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"node -s\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-l\", *args) || streq(\"--layer\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstack_layer_t lyr;\n\t\t\tif (parse_stack_layer(*args, &lyr)) {\n\t\t\t\tif (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-t\", *args) || streq(\"--state\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tclient_state_t cst;\n\t\t\tbool alternate = false;\n\t\t\tif ((*args)[0] == '~') {\n\t\t\t\talternate = true;\n\t\t\t\t(*args)++;\n\t\t\t}\n\t\t\tif (alternate && (*args)[0] == '\\0') {\n\t\t\t\tif (trg.node != NULL && trg.node->client != NULL) {\n\t\t\t\t\tcst = trg.node->client->last_state;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if (parse_client_state(*args, &cst)) {\n\t\t\t\tif (alternate && trg.node != NULL && trg.node->client != NULL &&\n\t\t\t\t    trg.node->client->state == cst) {\n\t\t\t\t\tcst = trg.node->client->last_state;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tchanged = true;\n\t\t} else if (streq(\"-g\", *args) || streq(\"--flag\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tchar *key = strtok(*args, EQL_TOK);\n\t\t\tchar *val = strtok(NULL, EQL_TOK);\n\t\t\talter_state_t a;\n\t\t\tbool b;\n\t\t\tif (val == NULL) {\n\t\t\t\ta = ALTER_TOGGLE;\n\t\t\t} else {\n\t\t\t\tif (parse_bool(val, &b)) {\n\t\t\t\t\ta = ALTER_SET;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid value for %s: '%s'.\\n\", *(args - 1), key, val);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (streq(\"hidden\", key)) {\n\t\t\t\tset_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));\n\t\t\t\tchanged = true;\n\t\t\t} else if (streq(\"sticky\", key)) {\n\t\t\t\tset_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));\n\t\t\t} else if (streq(\"private\", key)) {\n\t\t\t\tset_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));\n\t\t\t} else if (streq(\"locked\", key)) {\n\t\t\t\tset_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));\n\t\t\t} else if (streq(\"marked\", key)) {\n\t\t\t\tset_marked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->marked));\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid key: '%s'.\\n\", *(args - 1), key);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-p\", *args) || streq(\"--presel-dir\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL || trg.node->vacant) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (streq(\"cancel\", *args)) {\n\t\t\t\tcancel_presel(trg.monitor, trg.desktop, trg.node);\n\t\t\t} else {\n\t\t\t\tbool alternate = false;\n\t\t\t\tif ((*args)[0] == '~') {\n\t\t\t\t\talternate = true;\n\t\t\t\t\t(*args)++;\n\t\t\t\t}\n\t\t\t\tdirection_t dir;\n\t\t\t\tif (parse_direction(*args, &dir)) {\n\t\t\t\t\tif (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {\n\t\t\t\t\t\tcancel_presel(trg.monitor, trg.desktop, trg.node);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpresel_dir(trg.monitor, trg.desktop, trg.node, dir);\n\t\t\t\t\t\tif (!IS_RECEPTACLE(trg.node)) {\n\t\t\t\t\t\t\tdraw_presel_feedback(trg.monitor, trg.desktop, trg.node);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s%s'.\\n\", *(args - 1), alternate?\"~\":\"\", *args);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"-o\", *args) || streq(\"--presel-ratio\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL || trg.node->vacant) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdouble rat;\n\t\t\tif (sscanf(*args, \"%lf\", &rat) != 1 || rat <= 0 || rat >= 1) {\n\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tpresel_ratio(trg.monitor, trg.desktop, trg.node, rat);\n\t\t\t\tdraw_presel_feedback(trg.monitor, trg.desktop, trg.node);\n\t\t\t}\n\t\t} else if (streq(\"-v\", *args) || streq(\"--move\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 2) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tint dx = 0, dy = 0;\n\t\t\tif (sscanf(*args, \"%i\", &dx) == 1) {\n\t\t\t\tnum--, args++;\n\t\t\t\tif (sscanf(*args, \"%i\", &dy) == 1) {\n\t\t\t\t\tif (!move_client(&trg, dx, dy)) {\n\t\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid dy argument: '%s'.\\n\", *(args - 3), *args);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid dx argument: '%s'.\\n\", *(args - 2), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-z\", *args) || streq(\"--resize\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 3) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tresize_handle_t rh;\n\t\t\tif (parse_resize_handle(*args, &rh)) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint dx = 0, dy = 0;\n\t\t\t\tif (sscanf(*args, \"%i\", &dx) == 1) {\n\t\t\t\t\tnum--, args++;\n\t\t\t\t\tif (sscanf(*args, \"%i\", &dy) == 1) {\n\t\t\t\t\t\tif (!resize_client(&trg, rh, dx, dy, true)) {\n\t\t\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfail(rsp, \"node %s: Invalid dy argument: '%s'.\\n\", *(args - 3), *args);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid dx argument: '%s'.\\n\", *(args - 2), *args);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid resize handle argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-y\", *args) || streq(\"--type\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcycle_dir_t cyc;\n\t\t\tsplit_type_t typ;\n\t\t\tif (parse_cycle_direction(*args, &cyc)) {\n\t\t\t\tset_type(trg.node, (trg.node->split_type + 1) % 2);\n\t\t\t\tchanged = true;\n\t\t\t} else if (parse_split_type(*args, &typ)) {\n\t\t\t\tchanged |= set_type(trg.node, typ);\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-r\", *args) || streq(\"--ratio\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ((*args)[0] == '+' || (*args)[0] == '-') {\n\t\t\t\tfloat delta;\n\t\t\t\tif (sscanf(*args, \"%f\", &delta) == 1) {\n\t\t\t\t\tdouble rat = trg.node->split_ratio;\n\t\t\t\t\tif (delta > -1 && delta < 1) {\n\t\t\t\t\t\trat += delta;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);\n\t\t\t\t\t\trat = ((max * rat) + delta) / max;\n\t\t\t\t\t}\n\t\t\t\t\tif (rat > 0 && rat < 1) {\n\t\t\t\t\t\tchanged |= set_ratio(trg.node, rat);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdouble rat;\n\t\t\t\tif (sscanf(*args, \"%lf\", &rat) == 1 && rat > 0 && rat < 1) {\n\t\t\t\t\tchanged |= set_ratio(trg.node, rat);\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"-F\", *args) || streq(\"--flip\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tflip_t flp;\n\t\t\tif (parse_flip(*args, &flp)) {\n\t\t\t\tflip_tree(trg.node, flp);\n\t\t\t\tchanged = true;\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-R\", *args) || streq(\"--rotate\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tint deg;\n\t\t\tif (parse_degree(*args, &deg)) {\n\t\t\t\trotate_tree(trg.node, deg);\n\t\t\t\tchanged = true;\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-E\", *args) || streq(\"--equalize\", *args)) {\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tequalize_tree(trg.node);\n\t\t\tchanged = true;\n\t\t} else if (streq(\"-B\", *args) || streq(\"--balance\", *args)) {\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbalance_tree(trg.node);\n\t\t\tchanged = true;\n\t\t} else if (streq(\"-C\", *args) || streq(\"--circulate\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"node %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcirculate_dir_t cir;\n\t\t\tif (parse_circulate_direction(*args, &cir)) {\n\t\t\t\tcirculate_leaves(trg.monitor, trg.desktop, trg.node, cir);\n\t\t\t\tchanged = true;\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"node %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-i\", *args) || streq(\"--insert-receptacle\", *args)) {\n\t\t\tinsert_receptacle(trg.monitor, trg.desktop, trg.node);\n\t\t\tchanged = true;\n\t\t} else if (streq(\"-c\", *args) || streq(\"--close\", *args)) {\n\t\t\tif (num > 1) {\n\t\t\t\tfail(rsp, \"node %s: Trailing commands.\\n\", *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL || locked_count(trg.node) > 0) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tclose_node(trg.node);\n\t\t\tbreak;\n\t\t} else if (streq(\"-k\", *args) || streq(\"--kill\", *args)) {\n\t\t\tif (num > 1) {\n\t\t\t\tfail(rsp, \"node %s: Trailing commands.\\n\", *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.node == NULL) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tkill_node(trg.monitor, trg.desktop, trg.node);\n\t\t\tchanged = true;\n\t\t\tbreak;\n\t\t} else {\n\t\t\tfail(rsp, \"node: Unknown command: '%s'.\\n\", *args);\n\t\t\tbreak;\n\t\t}\n\n\t\tnum--, args++;\n\t}\n\n\tif (changed) {\n\t\tarrange(trg.monitor, trg.desktop);\n\t}\n}\n\nvoid cmd_desktop(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"desktop: Missing arguments.\\n\");\n\t\treturn;\n\t}\n\n\tcoordinates_t ref = {mon, mon->desk, NULL};\n\tcoordinates_t trg = ref;\n\n\tif ((*args)[0] != OPT_CHR) {\n\t\tint ret;\n\t\tif ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {\n\t\t\tnum--, args++;\n\t\t} else {\n\t\t\thandle_failure(ret, \"desktop\", *args, rsp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (num < 1) {\n\t\tfail(rsp, \"desktop: Missing commands.\\n\");\n\t\treturn;\n\t}\n\n\tbool changed = false;\n\n\twhile (num > 0) {\n\t\tif (streq(\"-f\", *args) || streq(\"--focus\", *args)) {\n\t\t\tcoordinates_t dst = trg;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"desktop -f\", *args, rsp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfocus_node(dst.monitor, dst.desktop, NULL);\n\t\t} else if (streq(\"-a\", *args) || streq(\"--activate\", *args)) {\n\t\t\tcoordinates_t dst = trg;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"desktop -a\", *args, rsp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (activate_desktop(dst.monitor, dst.desktop)) {\n\t\t\t\tactivate_node(dst.monitor, dst.desktop, NULL);\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-m\", *args) || streq(\"--to-monitor\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.monitor->desk_head == trg.monitor->desk_tail) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (transfer_desktop(trg.monitor, dst.monitor, trg.desktop, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"desktop -m\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-s\", *args) || streq(\"--swap\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tbool follow = false;\n\t\t\t\tif (num > 1 && streq(\"--follow\", *(args+1))) {\n\t\t\t\t\tfollow = true;\n\t\t\t\t\tnum--, args++;\n\t\t\t\t}\n\t\t\t\tif (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop, follow)) {\n\t\t\t\t\ttrg.monitor = dst.monitor;\n\t\t\t\t} else {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"desktop -s\", *args, rsp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-b\", *args) || streq(\"--bubble\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcycle_dir_t cyc;\n\t\t\tif (parse_cycle_direction(*args, &cyc)) {\n\t\t\t\tdesktop_t *d = trg.desktop;\n\t\t\t\tif (cyc == CYCLE_PREV) {\n\t\t\t\t\tif (d->prev == NULL) {\n\t\t\t\t\t\twhile (d->next != NULL) {\n\t\t\t\t\t\t\tswap_desktops(trg.monitor, d, trg.monitor, d->next, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tswap_desktops(trg.monitor, d, trg.monitor, d->prev, false);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (d->next == NULL) {\n\t\t\t\t\t\twhile (d->prev != NULL) {\n\t\t\t\t\t\t\tswap_desktops(trg.monitor, d, trg.monitor, d->prev, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tswap_desktops(trg.monitor, d, trg.monitor, d->next, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"desktop %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-l\", *args) || streq(\"--layout\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbool ret;\n\t\t\tlayout_t lyt;\n\t\t\tcycle_dir_t cyc;\n\t\t\tif (parse_cycle_direction(*args, &cyc)) {\n\t\t\t\tret = set_layout(trg.monitor, trg.desktop, (trg.desktop->user_layout + 1) % 2, true);\n\t\t\t} else if (parse_layout(*args, &lyt)) {\n\t\t\t\tret = set_layout(trg.monitor, trg.desktop, lyt, true);\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"desktop %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!ret) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-n\", *args) || streq(\"--rename\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trename_desktop(trg.monitor, trg.desktop, *args);\n\t\t} else if (streq(\"-r\", *args) || streq(\"--remove\", *args)) {\n\t\t\tif (num > 1) {\n\t\t\t\tfail(rsp, \"desktop %s: Trailing commands.\\n\", *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (trg.monitor->desk_head != trg.monitor->desk_tail) {\n\t\t\t\tdesktop_t *fallback = trg.desktop->prev == NULL ?\n\t\t\t\t                      trg.desktop->next :\n\t\t\t\t                      trg.desktop->prev;\n\t\t\t\tmerge_desktops(trg.monitor, trg.desktop, trg.monitor, fallback);\n\t\t\t\tremove_desktop(trg.monitor, trg.desktop);\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tfail(rsp, \"desktop: Unknown command: '%s'.\\n\", *args);\n\t\t\tbreak;\n\t\t}\n\t\tnum--, args++;\n\t}\n\n\tif (changed) {\n\t\tarrange(trg.monitor, trg.desktop);\n\t}\n}\n\nvoid cmd_monitor(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"monitor: Missing arguments.\\n\");\n\t\treturn;\n\t}\n\n\tcoordinates_t ref = {mon, NULL, NULL};\n\tcoordinates_t trg = ref;\n\n\tif ((*args)[0] != OPT_CHR) {\n\t\tint ret;\n\t\tif ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {\n\t\t\tnum--, args++;\n\t\t} else {\n\t\t\thandle_failure(ret, \"monitor\", *args, rsp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (num < 1) {\n\t\tfail(rsp, \"monitor: Missing commands.\\n\");\n\t\treturn;\n\t}\n\n\twhile (num > 0) {\n\t\tif (streq(\"-f\", *args) || streq(\"--focus\", *args)) {\n\t\t\tcoordinates_t dst = trg;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"monitor -f\", *args, rsp);\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfocus_node(dst.monitor, NULL, NULL);\n\t\t} else if (streq(\"-s\", *args) || streq(\"--swap\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcoordinates_t dst;\n\t\t\tint ret;\n\t\t\tif ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {\n\t\t\t\tif (!swap_monitors(trg.monitor, dst.monitor)) {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\thandle_failure(ret, \"monitor -s\", *args, rsp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (streq(\"-d\", *args) || streq(\"--reset-desktops\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdesktop_t *d = trg.monitor->desk_head;\n\t\t\twhile (num > 0 && d != NULL) {\n\t\t\t\trename_desktop(trg.monitor, d, *args);\n\t\t\t\td = d->next;\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t\tput_status(SBSC_MASK_REPORT);\n\t\t\twhile (num > 0) {\n\t\t\t\tadd_desktop(trg.monitor, make_desktop(*args, XCB_NONE));\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t\twhile (d != NULL) {\n\t\t\t\tdesktop_t *next = d->next;\n\t\t\t\tif (d == mon->desk) {\n\t\t\t\t\tfocus_node(trg.monitor, d->prev, d->prev->focus);\n\t\t\t\t}\n\t\t\t\tmerge_desktops(trg.monitor, d, mon, mon->desk);\n\t\t\t\tremove_desktop(trg.monitor, d);\n\t\t\t\td = next;\n\t\t\t}\n\t\t} else if (streq(\"-a\", *args) || streq(\"--add-desktops\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twhile (num > 0) {\n\t\t\t\tadd_desktop(trg.monitor, make_desktop(*args, XCB_NONE));\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t} else if (streq(\"-r\", *args) || streq(\"--remove\", *args)) {\n\t\t\tif (num > 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Trailing commands.\\n\", *args);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (mon_head == mon_tail) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tremove_monitor(trg.monitor);\n\t\t\treturn;\n\t\t} else if (streq(\"-o\", *args) || streq(\"--reorder-desktops\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdesktop_t *d = trg.monitor->desk_head;\n\t\t\twhile (d != NULL && num > 0) {\n\t\t\t\tdesktop_t *next = d->next;\n\t\t\t\tcoordinates_t dst;\n\t\t\t\tif (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {\n\t\t\t\t\tswap_desktops(trg.monitor, d, dst.monitor, dst.desktop, false);\n\t\t\t\t\tif (next == dst.desktop) {\n\t\t\t\t\t\tnext = d;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\td = next;\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t} else if (streq(\"-g\", *args) || streq(\"--rectangle\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\txcb_rectangle_t r;\n\t\t\tif (parse_rectangle(*args, &r)) {\n\t\t\t\tupdate_root(trg.monitor, &r);\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"monitor %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (streq(\"-n\", *args) || streq(\"--rename\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"monitor %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trename_monitor(trg.monitor, *args);\n\t\t} else {\n\t\t\tfail(rsp, \"monitor: Unknown command: '%s'.\\n\", *args);\n\t\t\treturn;\n\t\t}\n\t\tnum--, args++;\n\t}\n}\n\nvoid cmd_query(char **args, int num, FILE *rsp)\n{\n\tcoordinates_t monitor_ref = {mon, NULL, NULL};\n\tcoordinates_t desktop_ref = {mon, mon->desk, NULL};\n\tcoordinates_t node_ref = {mon, mon->desk, mon->desk->focus};\n\tcoordinates_t trg = {NULL, NULL, NULL};\n\tmonitor_select_t *monitor_sel = NULL;\n\tdesktop_select_t *desktop_sel = NULL;\n\tnode_select_t *node_sel = NULL;\n\tdomain_t dom = DOMAIN_TREE;\n\tbool print_ids = true;\n\tuint8_t d = 0;\n\n\tif (num < 1) {\n\t\tfail(rsp, \"query: Missing arguments.\\n\");\n\t\treturn;\n\t}\n\n\twhile (num > 0) {\n\t\tif (streq(\"-T\", *args) || streq(\"--tree\", *args)) {\n\t\t\tdom = DOMAIN_TREE, d++;\n\t\t} else if (streq(\"-M\", *args) || streq(\"--monitors\", *args)) {\n\t\t\tdom = DOMAIN_MONITOR, d++;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tcoordinates_t tmp = monitor_ref;\n\t\t\t\tmonitor_ref.desktop = NULL;\n\t\t\t\tmonitor_ref.node = NULL;\n\t\t\t\tif ((ret = monitor_from_desc(*args, &tmp, &monitor_ref)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -M\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"-D\", *args) || streq(\"--desktops\", *args)) {\n\t\t\tdom = DOMAIN_DESKTOP, d++;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tcoordinates_t tmp = desktop_ref;\n\t\t\t\tdesktop_ref.node = NULL;\n\t\t\t\tif ((ret = desktop_from_desc(*args, &tmp, &desktop_ref)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -D\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"-N\", *args) || streq(\"--nodes\", *args)) {\n\t\t\tdom = DOMAIN_NODE, d++;\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tcoordinates_t tmp = node_ref;\n\t\t\t\tif ((ret = node_from_desc(*args, &tmp, &node_ref)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -N\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"-m\", *args) || streq(\"--monitor\", *args)) {\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((*args)[0] == '.') {\n\t\t\t\t\tfree(monitor_sel);\n\t\t\t\t\tmonitor_sel = malloc(sizeof(monitor_select_t));\n\t\t\t\t\t*monitor_sel = make_monitor_select();\n\t\t\t\t\tchar *desc = copy_string(*args, strlen(*args));\n\t\t\t\t\tif (!parse_monitor_modifiers(desc, monitor_sel)) {\n\t\t\t\t\t\thandle_failure(SELECTOR_BAD_MODIFIERS, \"query -m\", *args, rsp);\n\t\t\t\t\t\tfree(desc);\n\t\t\t\t\t\tgoto end;\n\t\t\t\t\t}\n\t\t\t\t\tfree(desc);\n\t\t\t\t} else if ((ret = monitor_from_desc(*args, &monitor_ref, &trg)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -m\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttrg = monitor_ref;\n\t\t\t}\n\t\t} else if (streq(\"-d\", *args) || streq(\"--desktop\", *args)) {\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((*args)[0] == '.') {\n\t\t\t\t\tfree(desktop_sel);\n\t\t\t\t\tdesktop_sel = malloc(sizeof(desktop_select_t));\n\t\t\t\t\t*desktop_sel = make_desktop_select();\n\t\t\t\t\tchar *desc = copy_string(*args, strlen(*args));\n\t\t\t\t\tif (!parse_desktop_modifiers(desc, desktop_sel)) {\n\t\t\t\t\t\thandle_failure(SELECTOR_BAD_MODIFIERS, \"query -d\", *args, rsp);\n\t\t\t\t\t\tfree(desc);\n\t\t\t\t\t\tgoto end;\n\t\t\t\t\t}\n\t\t\t\t\tfree(desc);\n\t\t\t\t} else if ((ret = desktop_from_desc(*args, &desktop_ref, &trg)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -d\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttrg = desktop_ref;\n\t\t\t}\n\t\t} else if (streq(\"-n\", *args) || streq(\"--node\", *args)) {\n\t\t\tif (num > 1 && *(args + 1)[0] != OPT_CHR) {\n\t\t\t\tnum--, args++;\n\t\t\t\tint ret;\n\t\t\t\tif ((*args)[0] == '.') {\n\t\t\t\t\tfree(node_sel);\n\t\t\t\t\tnode_sel = malloc(sizeof(node_select_t));\n\t\t\t\t\t*node_sel = make_node_select();\n\t\t\t\t\tchar *desc = copy_string(*args, strlen(*args));\n\t\t\t\t\tif (!parse_node_modifiers(desc, node_sel)) {\n\t\t\t\t\t\thandle_failure(SELECTOR_BAD_MODIFIERS, \"query -n\", *args, rsp);\n\t\t\t\t\t\tfree(desc);\n\t\t\t\t\t\tgoto end;\n\t\t\t\t\t}\n\t\t\t\t\tfree(desc);\n\t\t\t\t} else if ((ret = node_from_desc(*args, &node_ref, &trg)) != SELECTOR_OK) {\n\t\t\t\t\thandle_failure(ret, \"query -n\", *args, rsp);\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttrg = node_ref;\n\t\t\t\tif (trg.node == NULL) {\n\t\t\t\t\tfail(rsp, \"\");\n\t\t\t\t\tgoto end;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (streq(\"--names\", *args)) {\n\t\t\tprint_ids = false;\n\t\t} else {\n\t\t\tfail(rsp, \"query: Unknown option: '%s'.\\n\", *args);\n\t\t\tgoto end;\n\t\t}\n\t\tnum--, args++;\n\t}\n\n\tif (d < 1) {\n\t\tfail(rsp, \"query: No commands given.\\n\");\n\t\tgoto end;\n\t}\n\n\tif (d > 1) {\n\t\tfail(rsp, \"query: Multiple commands given.\\n\");\n\t\tgoto end;\n\t}\n\n\tif (dom == DOMAIN_TREE && trg.monitor == NULL) {\n\t\tfail(rsp, \"query -T: No options given.\\n\");\n\t\tgoto end;\n\t}\n\n\tif (!print_ids && (dom == DOMAIN_NODE || dom == DOMAIN_TREE)) {\n\t\tfail(rsp, \"query -%c: --names only applies to -M and -D.\\n\", dom == DOMAIN_NODE ? 'N' : 'T');\n\t\tgoto end;\n\t}\n\n\tif ((dom == DOMAIN_MONITOR && (desktop_sel != NULL || node_sel != NULL)) ||\n\t    (dom == DOMAIN_DESKTOP && node_sel != NULL)) {\n\t\tfail(rsp, \"query -%c: Incompatible descriptor-free constraints.\\n\", dom == DOMAIN_MONITOR ? 'M' : 'D');\n\t\tgoto end;\n\t}\n\n\tif (dom == DOMAIN_NODE) {\n\t\tif (query_node_ids(&monitor_ref, &desktop_ref, &node_ref, &trg, monitor_sel, desktop_sel, node_sel, rsp) < 1) {\n\t\t\tfail(rsp, \"\");\n\t\t}\n\t} else if (dom == DOMAIN_DESKTOP) {\n\t\tif (query_desktop_ids(&monitor_ref, &desktop_ref, &trg, monitor_sel, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {\n\t\t\tfail(rsp, \"\");\n\t\t}\n\t} else if (dom == DOMAIN_MONITOR) {\n\t\tif (query_monitor_ids(&monitor_ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {\n\t\t\tfail(rsp, \"\");\n\t\t}\n\t} else {\n\t\tif (trg.node != NULL) {\n\t\t\tquery_node(trg.node, rsp);\n\t\t} else if (trg.desktop != NULL) {\n\t\t\tquery_desktop(trg.desktop, rsp);\n\t\t} else  {\n\t\t\tquery_monitor(trg.monitor, rsp);\n\t\t}\n\t\tfprintf(rsp, \"\\n\");\n\t}\n\nend:\n\tfree(monitor_sel);\n\tfree(desktop_sel);\n\tfree(node_sel);\n}\n\nvoid cmd_rule(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"rule: Missing commands.\\n\");\n\t\treturn;\n\t}\n\n\twhile (num > 0) {\n\t\tif (streq(\"-a\", *args) || streq(\"--add\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 2) {\n\t\t\t\tfail(rsp, \"rule %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trule_t *rule = make_rule();\n\n\t\t\tstruct tokenize_state state;\n\t\t\tchar *class_name = tokenize_with_escape(&state, args[0], COL_TOK[0]);\n\t\t\tchar *instance_name = tokenize_with_escape(&state, NULL, COL_TOK[0]);\n\t\t\tchar *name = tokenize_with_escape(&state, NULL, COL_TOK[0]);\n\t\t\tif (!class_name || !instance_name || !name) {\n\t\t\t\tfree(class_name);\n\t\t\t\tfree(instance_name);\n\t\t\t\tfree(name);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsnprintf(rule->class_name, sizeof(rule->class_name), \"%s\", class_name);\n\t\t\tsnprintf(rule->instance_name, sizeof(rule->instance_name), \"%s\",\n\t\t\t\t\t instance_name[0] == '\\0' ? MATCH_ANY : instance_name);\n\t\t\tsnprintf(rule->name, sizeof(rule->name), \"%s\",\n\t\t\t\t\t name[0] == '\\0' ? MATCH_ANY : name);\n\t\t\tfree(class_name);\n\t\t\tfree(instance_name);\n\t\t\tfree(name);\n\n\t\t\tnum--, args++;\n\t\t\tsize_t i = 0;\n\t\t\twhile (num > 0) {\n\t\t\t\tif (streq(\"-o\", *args) || streq(\"--one-shot\", *args)) {\n\t\t\t\t\trule->one_shot = true;\n\t\t\t\t} else {\n\t\t\t\t\tfor (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {\n\t\t\t\t\t\trule->effect[i] = (*args)[j];\n\t\t\t\t\t}\n\t\t\t\t\tif (num > 1 && i < sizeof(rule->effect)) {\n\t\t\t\t\t\trule->effect[i++] = ' ';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t\trule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\\0';\n\t\t\tadd_rule(rule);\n\t\t} else if (streq(\"-r\", *args) || streq(\"--remove\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"rule %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tuint16_t idx;\n\t\t\twhile (num > 0) {\n\t\t\t\tif (parse_index(*args, &idx)) {\n\t\t\t\t\tremove_rule_by_index(idx - 1);\n\t\t\t\t} else if (streq(\"tail\", *args)) {\n\t\t\t\t\tremove_rule(rule_tail);\n\t\t\t\t} else if (streq(\"head\", *args)) {\n\t\t\t\t\tremove_rule(rule_head);\n\t\t\t\t} else {\n\t\t\t\t\tremove_rule_by_cause(*args);\n\t\t\t\t}\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t} else if (streq(\"-l\", *args) || streq(\"--list\", *args)) {\n\t\t\tlist_rules(rsp);\n\t\t} else {\n\t\t\tfail(rsp, \"rule: Unknown command: '%s'.\\n\", *args);\n\t\t\treturn;\n\t\t}\n\t\tnum--, args++;\n\t}\n}\n\nvoid cmd_wm(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"wm: Missing commands.\\n\");\n\t\treturn;\n\t}\n\n\twhile (num > 0) {\n\t\tif (streq(\"-d\", *args) || streq(\"--dump-state\", *args)) {\n\t\t\tquery_state(rsp);\n\t\t\tfprintf(rsp, \"\\n\");\n\t\t} else if (streq(\"-l\", *args) || streq(\"--load-state\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"wm %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!restore_state(*args)) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-a\", *args) || streq(\"--add-monitor\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 2) {\n\t\t\t\tfail(rsp, \"wm %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tchar *name = *args;\n\t\t\tnum--, args++;\n\t\t\txcb_rectangle_t r;\n\t\t\tif (parse_rectangle(*args, &r)) {\n\t\t\t\tmonitor_t *m = make_monitor(name, &r, XCB_NONE);\n\t\t\t\tadd_monitor(m);\n\t\t\t\tadd_desktop(m, make_desktop(NULL, XCB_NONE));\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"wm %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-O\", *args) || streq(\"--reorder-monitors\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"wm %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmonitor_t *m = mon_head;\n\t\t\twhile (m != NULL && num > 0) {\n\t\t\t\tmonitor_t *next = m->next;\n\t\t\t\tcoordinates_t dst;\n\t\t\t\tif (locate_monitor(*args, &dst)) {\n\t\t\t\t\tswap_monitors(m, dst.monitor);\n\t\t\t\t\tif (next == dst.monitor) {\n\t\t\t\t\t\tnext = m;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tm = next;\n\t\t\t\tnum--, args++;\n\t\t\t}\n\t\t} else if (streq(\"-o\", *args) || streq(\"--adopt-orphans\", *args)) {\n\t\t\tadopt_orphans();\n\t\t} else if (streq(\"-g\", *args) || streq(\"--get-status\", *args)) {\n\t\t\tprint_report(rsp);\n\t\t} else if (streq(\"-h\", *args) || streq(\"--record-history\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"wm %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbool b;\n\t\t\tif (parse_bool(*args, &b)) {\n\t\t\t\trecord_history = b;\n\t\t\t} else {\n\t\t\t\tfail(rsp, \"wm %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (streq(\"-r\", *args) || streq(\"--restart\", *args)) {\n\t\t\trunning = false;\n\t\t\trestart = true;\n\t\t\tbreak;\n\t\t} else {\n\t\t\tfail(rsp, \"wm: Unknown command: '%s'.\\n\", *args);\n\t\t\tbreak;\n\t\t}\n\t\tnum--, args++;\n\t}\n}\n\nvoid cmd_subscribe(char **args, int num, FILE *rsp)\n{\n\tint field = 0;\n\tint count = -1;\n\tFILE *stream = rsp;\n\tchar *fifo_path = NULL;\n\tsubscriber_mask_t mask;\n\n\twhile (num > 0) {\n\t\tif (streq(\"-c\", *args) || streq(\"--count\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"subscribe %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\tgoto failed;\n\t\t\t}\n\t\t\tif (sscanf(*args, \"%i\", &count) != 1 || count < 1) {\n\t\t\t\tfail(rsp, \"subscribe %s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\t\t\tgoto failed;\n\t\t\t}\n\t\t} else if (streq(\"-f\", *args) || streq(\"--fifo\", *args)) {\n\t\t\tfifo_path = mktempfifo(FIFO_TEMPLATE);\n\t\t\tif (fifo_path == NULL) {\n\t\t\t\tfail(rsp, \"subscribe %s: Can't create FIFO.\\n\", *(args - 1));\n\t\t\t\tgoto failed;\n\t\t\t}\n\t\t} else if (parse_subscriber_mask(*args, &mask)) {\n\t\t\tfield |= mask;\n\t\t} else {\n\t\t\tfail(rsp, \"subscribe: Invalid argument: '%s'.\\n\", *args);\n\t\t\tgoto failed;\n\t\t}\n\t\tnum--, args++;\n\t}\n\n\tif (field == 0) {\n\t\tfield = SBSC_MASK_REPORT;\n\t}\n\n\tif (fifo_path) {\n\t\tfprintf(rsp, \"%s\\n\", fifo_path);\n\t\tfflush(rsp);\n\t\tfclose(rsp);\n\n\t\tstream = fopen(fifo_path, \"w\");\n\n\t\tif (stream == NULL) {\n\t\t\tperror(\"subscribe: fopen\");\n\t\t\tgoto free_fifo_path;\n\t\t}\n\t}\n\n\tsubscriber_list_t *sb = make_subscriber(stream, fifo_path, field, count);\n\tadd_subscriber(sb);\n\treturn;\n\nfailed:\n\tfflush(rsp);\n\tfclose(rsp);\n\nfree_fifo_path:\n\tif (fifo_path) {\n\t\tunlink(fifo_path);\n\t\tfree(fifo_path);\n\t}\n}\n\nvoid cmd_quit(char **args, int num, FILE *rsp)\n{\n\tif (num > 0 && sscanf(*args, \"%i\", &exit_status) != 1) {\n\t\tfail(rsp, \"%s: Invalid argument: '%s'.\\n\", *(args - 1), *args);\n\t\treturn;\n\t}\n\trunning = false;\n}\n\nvoid cmd_config(char **args, int num, FILE *rsp)\n{\n\tif (num < 1) {\n\t\tfail(rsp, \"config: Missing arguments.\\n\");\n\t\treturn;\n\t}\n\n\tcoordinates_t ref = {mon, mon->desk, mon->desk->focus};\n\tcoordinates_t trg = {NULL, NULL, NULL};\n\n\twhile (num > 0 && (*args)[0] == OPT_CHR) {\n\t\tif (streq(\"-m\", *args) || streq(\"--monitor\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"config %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint ret;\n\t\t\tif ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {\n\t\t\t\thandle_failure(ret, \"config -m\", *args, rsp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (streq(\"-d\", *args) || streq(\"--desktop\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"config %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint ret;\n\t\t\tif ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {\n\t\t\t\thandle_failure(ret, \"config -d\", *args, rsp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (streq(\"-n\", *args) || streq(\"--node\", *args)) {\n\t\t\tnum--, args++;\n\t\t\tif (num < 1) {\n\t\t\t\tfail(rsp, \"config %s: Not enough arguments.\\n\", *(args - 1));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint ret;\n\t\t\tif ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {\n\t\t\t\thandle_failure(ret, \"config -n\", *args, rsp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tfail(rsp, \"config: Unknown option: '%s'.\\n\", *args);\n\t\t\treturn;\n\t\t}\n\t\tnum--, args++;\n\t}\n\tif (num == 2) {\n\t\tset_setting(trg, *args, *(args + 1), rsp);\n\t} else if (num == 1) {\n\t\tget_setting(trg, *args, rsp);\n\t} else {\n\t\tfail(rsp, \"config: Was expecting 1 or 2 arguments, received %i.\\n\", num);\n\t}\n}\n\nvoid set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)\n{\n\tbool colors_changed = false;\n#define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \\\n\t\tif (loc.node != NULL) { \\\n\t\t\tfor (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \\\n\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else if (loc.desktop != NULL) { \\\n\t\t\tloc.desktop->k = v; \\\n\t\t\tfor (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \\\n\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else if (loc.monitor != NULL) { \\\n\t\t\tloc.monitor->k = v; \\\n\t\t\tfor (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\td->k = v; \\\n\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \\\n\t\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t\t} \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else { \\\n\t\t\tk = v; \\\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) { \\\n\t\t\t\tm->k = v; \\\n\t\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\t\td->k = v; \\\n\t\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \\\n\t\t\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t\t\t} \\\n\t\t\t\t\t} \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t}\n\tif (streq(\"border_width\", name)) {\n\t\tunsigned int bw;\n\t\tif (sscanf(value, \"%u\", &bw) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)\n#undef SET_DEF_DEFMON_DEFDESK_WIN\n#define SET_DEF_WIN(k, v) \\\n\t\tif (loc.node != NULL) { \\\n\t\t\tfor (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \\\n\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else if (loc.desktop != NULL) { \\\n\t\t\tfor (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \\\n\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else if (loc.monitor != NULL) { \\\n\t\t\tfor (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \\\n\t\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t\t} \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} else { \\\n\t\t\tk = v; \\\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) { \\\n\t\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \\\n\t\t\t\t\t\tif (n->client != NULL) { \\\n\t\t\t\t\t\t\tn->client->k = v; \\\n\t\t\t\t\t\t} \\\n\t\t\t\t\t} \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t}\n\t} else if (streq(\"honor_size_hints\", name)) {\n\t\thonor_size_hints_mode_t hsh;\n\t\tif (!parse_honor_size_hints_mode(value, &hsh)) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_WIN(honor_size_hints, hsh)\n#undef SET_DEF_WIN\n#define SET_DEF_DEFMON_DESK(k, v) \\\n\t\tif (loc.desktop != NULL) { \\\n\t\t\tloc.desktop->k = v; \\\n\t\t} else if (loc.monitor != NULL) { \\\n\t\t\tloc.monitor->k = v; \\\n\t\t\tfor (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\td->k = v; \\\n\t\t\t} \\\n\t\t} else { \\\n\t\t\tk = v; \\\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) { \\\n\t\t\t\tm->k = v; \\\n\t\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \\\n\t\t\t\t\td->k = v; \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t}\n\t} else if (streq(\"window_gap\", name)) {\n\t\tint wg;\n\t\tif (sscanf(value, \"%i\", &wg) != 1) {\n\t\t\tfail(rsp, \"\");\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_DEFMON_DESK(window_gap, wg)\n#undef SET_DEF_DEFMON_DESK\n#define SET_DEF_MON_DESK(k, v) \\\n\t\tif (loc.desktop != NULL) { \\\n\t\t\tloc.desktop->k = v; \\\n\t\t} else if (loc.monitor != NULL) { \\\n\t\t\tloc.monitor->k = v; \\\n\t\t} else { \\\n\t\t\tk = v; \\\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) { \\\n\t\t\t\tm->k = v; \\\n\t\t\t} \\\n\t\t}\n\t} else if (streq(\"top_padding\", name)) {\n\t\tint tp;\n\t\tif (sscanf(value, \"%i\", &tp) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_MON_DESK(padding.top, tp)\n\t} else if (streq(\"right_padding\", name)) {\n\t\tint rp;\n\t\tif (sscanf(value, \"%i\", &rp) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_MON_DESK(padding.right, rp)\n\t} else if (streq(\"bottom_padding\", name)) {\n\t\tint bp;\n\t\tif (sscanf(value, \"%i\", &bp) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_MON_DESK(padding.bottom, bp)\n\t} else if (streq(\"left_padding\", name)) {\n\t\tint lp;\n\t\tif (sscanf(value, \"%i\", &lp) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t\tSET_DEF_MON_DESK(padding.left, lp)\n#undef SET_DEF_MON_DESK\n\t} else if (streq(\"top_monocle_padding\", name)) {\n\t\tif (sscanf(value, \"%i\", &monocle_padding.top) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t}\n\t} else if (streq(\"right_monocle_padding\", name)) {\n\t\tif (sscanf(value, \"%i\", &monocle_padding.right) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t}\n\t} else if (streq(\"bottom_monocle_padding\", name)) {\n\t\tif (sscanf(value, \"%i\", &monocle_padding.bottom) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t}\n\t} else if (streq(\"left_monocle_padding\", name)) {\n\t\tif (sscanf(value, \"%i\", &monocle_padding.left) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t}\n#define SET_STR(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tif (snprintf(s, sizeof(s), \"%s\", value) < 0) { \\\n\t\t\tfail(rsp, \"\"); \\\n\t\t\treturn; \\\n\t\t}\n\tSET_STR(external_rules_command)\n\tSET_STR(status_prefix)\n#undef SET_STR\n\t} else if (streq(\"split_ratio\", name)) {\n\t\tdouble r;\n\t\tif (sscanf(value, \"%lf\", &r) == 1 && r > 0 && r < 1) {\n\t\t\tsplit_ratio = r;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value); \\\n\t\t\treturn;\n\t\t}\n\t\treturn;\n#define SET_COLOR(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tif (!is_hex_color(value)) { \\\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value); \\\n\t\t\treturn; \\\n\t\t} else { \\\n\t\t\tsnprintf(s, sizeof(s), \"%s\", value); \\\n\t\t\tcolors_changed = true; \\\n\t\t}\n\tSET_COLOR(normal_border_color)\n\tSET_COLOR(active_border_color)\n\tSET_COLOR(focused_border_color)\n\tSET_COLOR(presel_feedback_color)\n#undef SET_COLOR\n\t} else if (streq(\"initial_polarity\", name)) {\n\t\tchild_polarity_t p;\n\t\tif (parse_child_polarity(value, &p)) {\n\t\t\tinitial_polarity = p;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"automatic_scheme\", name)) {\n\t\tautomatic_scheme_t a;\n\t\tif (parse_automatic_scheme(value, &a)) {\n\t\t\tautomatic_scheme = a;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"mapping_events_count\", name)) {\n\t\tif (sscanf(value, \"%\" SCNi8, &mapping_events_count) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"directional_focus_tightness\", name)) {\n\t\ttightness_t p;\n\t\tif (parse_tightness(value, &p)) {\n\t\t\tdirectional_focus_tightness = p;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"ignore_ewmh_fullscreen\", name)) {\n\t\tstate_transition_t m;\n\t\tif (parse_state_transition(value, &m)) {\n\t\t\tignore_ewmh_fullscreen = m;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"pointer_modifier\", name)) {\n\t\tif (parse_modifier_mask(value, &pointer_modifier)) {\n\t\t\tungrab_buttons();\n\t\t\tgrab_buttons();\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"pointer_motion_interval\", name)) {\n\t\tif (sscanf(value, \"%u\", &pointer_motion_interval) != 1) {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"pointer_action1\", name) ||\n\t           streq(\"pointer_action2\", name) ||\n\t           streq(\"pointer_action3\", name)) {\n\t\tint index = name[14] - '1';\n\t\tif (parse_pointer_action(value, &pointer_actions[index])) {\n\t\t\tungrab_buttons();\n\t\t\tgrab_buttons();\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"click_to_focus\", name)) {\n\t\tif (parse_button_index(value, &click_to_focus)) {\n\t\t\tungrab_buttons();\n\t\t\tgrab_buttons();\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"single_monocle\", name)) {\n\t\tbool b;\n\t\tif (parse_bool(value, &b)) {\n\t\t\tif (b == single_monocle) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsingle_monocle = b;\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\t\t\tlayout_t l = (single_monocle && tiled_count(d->root, true) <= 1) ? LAYOUT_MONOCLE : d->user_layout;\n\t\t\t\t\tset_layout(m, d, l, false);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n\t} else if (streq(\"focus_follows_pointer\", name)) {\n\t\tbool b;\n\t\tif (parse_bool(value, &b)) {\n\t\t\tif (b == focus_follows_pointer) {\n\t\t\t\tfail(rsp, \"\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfocus_follows_pointer = b;\n\t\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\t\tif (focus_follows_pointer) {\n\t\t\t\t\twindow_show(m->root);\n\t\t\t\t} else {\n\t\t\t\t\twindow_hide(m->root);\n\t\t\t\t}\n\t\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\t\t\tlisten_enter_notify(d->root, focus_follows_pointer);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (focus_follows_pointer) {\n\t\t\t\tupdate_motion_recorder();\n\t\t\t} else {\n\t\t\t\tdisable_motion_recorder();\n\t\t\t}\n\t\t\treturn;\n\t\t} else {\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value);\n\t\t\treturn;\n\t\t}\n#define SET_BOOL(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tif (!parse_bool(value, &s)) { \\\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value); \\\n\t\t\treturn; \\\n\t\t}\n\t\tSET_BOOL(presel_feedback)\n\t\tSET_BOOL(borderless_monocle)\n\t\tSET_BOOL(gapless_monocle)\n\t\tSET_BOOL(borderless_singleton)\n\t\tSET_BOOL(swallow_first_click)\n\t\tSET_BOOL(pointer_follows_focus)\n\t\tSET_BOOL(pointer_follows_monitor)\n\t\tSET_BOOL(ignore_ewmh_focus)\n\t\tSET_BOOL(ignore_ewmh_struts)\n\t\tSET_BOOL(center_pseudo_tiled)\n\t\tSET_BOOL(removal_adjustment)\n#undef SET_BOOL\n#define SET_MON_BOOL(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tif (!parse_bool(value, &s)) { \\\n\t\t\tfail(rsp, \"config: %s: Invalid value: '%s'.\\n\", name, value); \\\n\t\t\treturn; \\\n\t\t} \\\n\t\tif (s) { \\\n\t\t\tupdate_monitors(); \\\n\t\t}\n\t\tSET_MON_BOOL(remove_disabled_monitors)\n\t\tSET_MON_BOOL(remove_unplugged_monitors)\n\t\tSET_MON_BOOL(merge_overlapping_monitors)\n#undef SET_MON_BOOL\n\t} else {\n\t\tfail(rsp, \"config: Unknown setting: '%s'.\\n\", name);\n\t\treturn;\n\t}\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tarrange(m, d);\n\t\t\tif (colors_changed) {\n\t\t\t\tupdate_colors_in(d->root, d, m);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid get_setting(coordinates_t loc, char *name, FILE* rsp)\n{\n\tif (streq(\"split_ratio\", name)) {\n\t\tfprintf(rsp, \"%lf\", split_ratio);\n\t} else if (streq(\"border_width\", name)) {\n\t\tif (loc.node != NULL) {\n\t\t\tfor (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {\n\t\t\t\tif (n->client != NULL) {\n\t\t\t\t\tfprintf(rsp, \"%u\", n->client->border_width);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (loc.desktop != NULL) {\n\t\t\tfprintf(rsp, \"%u\", loc.desktop->border_width);\n\t\t} else if (loc.monitor != NULL) {\n\t\t\tfprintf(rsp, \"%u\", loc.monitor->border_width);\n\t\t} else {\n\t\t\tfprintf(rsp, \"%u\", border_width);\n\t\t}\n\t} else if (streq(\"window_gap\", name)) {\n\t\tif (loc.desktop != NULL) {\n\t\t\tfprintf(rsp, \"%i\", loc.desktop->window_gap);\n\t\t} else if (loc.monitor != NULL) {\n\t\t\tfprintf(rsp, \"%i\", loc.monitor->window_gap);\n\t\t} else {\n\t\t\tfprintf(rsp, \"%i\", window_gap);\n\t\t}\n#define GET_DEF_MON_DESK(k) \\\n\t\tif (loc.desktop != NULL) { \\\n\t\t\tfprintf(rsp, \"%i\", loc.desktop->k); \\\n\t\t} else if (loc.monitor != NULL) { \\\n\t\t\tfprintf(rsp, \"%i\", loc.monitor->k); \\\n\t\t} else { \\\n\t\t\tfprintf(rsp, \"%i\", k); \\\n\t\t}\n\t} else if (streq(\"top_padding\", name)) {\n\t\tGET_DEF_MON_DESK(padding.top)\n\t} else if (streq(\"right_padding\", name)) {\n\t\tGET_DEF_MON_DESK(padding.right)\n\t} else if (streq(\"bottom_padding\", name)) {\n\t\tGET_DEF_MON_DESK(padding.bottom)\n\t} else if (streq(\"left_padding\", name)) {\n\t\tGET_DEF_MON_DESK(padding.left)\n#undef GET_DEF_MON_DESK\n\t} else if (streq(\"top_monocle_padding\", name)) {\n\t\tfprintf(rsp, \"%i\", monocle_padding.top);\n\t} else if (streq(\"right_monocle_padding\", name)) {\n\t\tfprintf(rsp, \"%i\", monocle_padding.right);\n\t} else if (streq(\"bottom_monocle_padding\", name)) {\n\t\tfprintf(rsp, \"%i\", monocle_padding.bottom);\n\t} else if (streq(\"left_monocle_padding\", name)) {\n\t\tfprintf(rsp, \"%i\", monocle_padding.left);\n\t} else if (streq(\"external_rules_command\", name)) {\n\t\tfprintf(rsp, \"%s\", external_rules_command);\n\t} else if (streq(\"status_prefix\", name)) {\n\t\tfprintf(rsp, \"%s\", status_prefix);\n\t} else if (streq(\"initial_polarity\", name)) {\n\t\tfprintf(rsp, \"%s\", CHILD_POL_STR(initial_polarity));\n\t} else if (streq(\"automatic_scheme\", name)) {\n\t\tfprintf(rsp, \"%s\", AUTO_SCM_STR(automatic_scheme));\n\t} else if (streq(\"honor_size_hints\", name)) {\n\t\tfprintf(rsp, \"%s\", HSH_MODE_STR(honor_size_hints));\n\t} else if (streq(\"mapping_events_count\", name)) {\n\t\tfprintf(rsp, \"%\" PRIi8, mapping_events_count);\n\t} else if (streq(\"directional_focus_tightness\", name)) {\n\t\tfprintf(rsp, \"%s\", TIGHTNESS_STR(directional_focus_tightness));\n\t} else if (streq(\"ignore_ewmh_fullscreen\", name)) {\n\t\tprint_ignore_request(ignore_ewmh_fullscreen, rsp);\n\t} else if (streq(\"pointer_modifier\", name)) {\n\t\tprint_modifier_mask(pointer_modifier, rsp);\n\t} else if (streq(\"click_to_focus\", name)) {\n\t\tprint_button_index(click_to_focus, rsp);\n\t} else if (streq(\"pointer_motion_interval\", name)) {\n\t\tfprintf(rsp, \"%u\", pointer_motion_interval);\n\t} else if (streq(\"pointer_action1\", name) ||\n\t           streq(\"pointer_action2\", name) ||\n\t           streq(\"pointer_action3\", name)) {\n\t\tint index = name[14] - '1';\n\t\tprint_pointer_action(pointer_actions[index], rsp);\n#define GET_COLOR(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tfprintf(rsp, \"%s\", s);\n\tGET_COLOR(normal_border_color)\n\tGET_COLOR(active_border_color)\n\tGET_COLOR(focused_border_color)\n\tGET_COLOR(presel_feedback_color)\n#undef GET_COLOR\n#define GET_BOOL(s) \\\n\t} else if (streq(#s, name)) { \\\n\t\tfprintf(rsp, \"%s\", BOOL_STR(s));\n\tGET_BOOL(presel_feedback)\n\tGET_BOOL(borderless_monocle)\n\tGET_BOOL(gapless_monocle)\n\tGET_BOOL(single_monocle)\n\tGET_BOOL(borderless_singleton)\n\tGET_BOOL(swallow_first_click)\n\tGET_BOOL(focus_follows_pointer)\n\tGET_BOOL(pointer_follows_focus)\n\tGET_BOOL(pointer_follows_monitor)\n\tGET_BOOL(ignore_ewmh_focus)\n\tGET_BOOL(ignore_ewmh_struts)\n\tGET_BOOL(center_pseudo_tiled)\n\tGET_BOOL(removal_adjustment)\n\tGET_BOOL(remove_disabled_monitors)\n\tGET_BOOL(remove_unplugged_monitors)\n\tGET_BOOL(merge_overlapping_monitors)\n#undef GET_BOOL\n\t} else {\n\t\tfail(rsp, \"config: Unknown setting: '%s'.\\n\", name);\n\t\treturn;\n\t}\n\tfprintf(rsp, \"\\n\");\n}\n\nvoid handle_failure(int code, char *src, char *val, FILE *rsp)\n{\n\tswitch (code) {\n\t\tcase SELECTOR_BAD_DESCRIPTOR:\n\t\t\tfail(rsp, \"%s: Invalid descriptor found in '%s'.\\n\", src, val);\n\t\t\tbreak;\n\t\tcase SELECTOR_BAD_MODIFIERS:\n\t\t\tfail(rsp, \"%s: Invalid modifier found in '%s'.\\n\", src, val);\n\t\t\tbreak;\n\t\tcase SELECTOR_INVALID:\n\t\t\tfail(rsp, \"\");\n\t\t\tbreak;\n\t}\n}\n\nvoid fail(FILE *rsp, char *fmt, ...)\n{\n\tfprintf(rsp, FAILURE_MESSAGE);\n\tva_list ap;\n\tva_start(ap, fmt);\n\tvfprintf(rsp, fmt, ap);\n\tva_end(ap);\n}\n"
  },
  {
    "path": "src/messages.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_MESSAGES_H\n#define BSPWM_MESSAGES_H\n\n#include \"types.h\"\n#include \"subscribe.h\"\n\nvoid handle_message(char *msg, int msg_len, FILE *rsp);\nvoid process_message(char **args, int num, FILE *rsp);\nvoid cmd_node(char **args, int num, FILE *rsp);\nvoid cmd_desktop(char **args, int num, FILE *rsp);\nvoid cmd_monitor(char **args, int num, FILE *rsp);\nvoid cmd_query(char **args, int num, FILE *rsp);\nvoid cmd_rule(char **args, int num, FILE *rsp);\nvoid cmd_wm(char **args, int num, FILE *rsp);\nvoid cmd_subscribe(char **args, int num, FILE *rsp);\nvoid cmd_quit(char **args, int num, FILE *rsp);\nvoid cmd_config(char **args, int num, FILE *rsp);\nvoid set_setting(coordinates_t loc, char *name, char *value, FILE *rsp);\nvoid get_setting(coordinates_t loc, char *name, FILE* rsp);\nvoid handle_failure(int code, char *src, char *val, FILE *rsp);\nvoid fail(FILE *rsp, char *fmt, ...);\n\n#endif\n"
  },
  {
    "path": "src/monitor.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <limits.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"ewmh.h\"\n#include \"query.h\"\n#include \"pointer.h\"\n#include \"settings.h\"\n#include \"geometry.h\"\n#include \"tree.h\"\n#include \"subscribe.h\"\n#include \"window.h\"\n#include \"monitor.h\"\n\nmonitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id)\n{\n\tmonitor_t *m = calloc(1, sizeof(monitor_t));\n\tif (id == XCB_NONE) {\n\t\tm->id = xcb_generate_id(dpy);\n\t}\n\tm->randr_id = XCB_NONE;\n\tsnprintf(m->name, sizeof(m->name), \"%s\", name == NULL ? DEFAULT_MON_NAME : name);\n\tm->padding = padding;\n\tm->border_width = border_width;\n\tm->window_gap = window_gap;\n\tm->root = XCB_NONE;\n\tm->prev = m->next = NULL;\n\tm->desk = m->desk_head = m->desk_tail = NULL;\n\tm->wired = true;\n\tm->sticky_count = 0;\n\tif (rect != NULL) {\n\t\tupdate_root(m, rect);\n\t} else {\n\t\tm->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};\n\t}\n\treturn m;\n}\n\nvoid update_root(monitor_t *m, xcb_rectangle_t *rect)\n{\n\txcb_rectangle_t last_rect = m->rectangle;\n\tm->rectangle = *rect;\n\tif (m->root == XCB_NONE) {\n\t\tuint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};\n\t\tm->root = xcb_generate_id(dpy);\n\t\txcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root,\n\t\t                  rect->x, rect->y, rect->width, rect->height, 0,\n\t\t                  XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);\n\t\txcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);\n\t\txcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);\n\t\txcb_ewmh_set_wm_window_type(ewmh, m->root, 1, &ewmh->_NET_WM_WINDOW_TYPE_DESKTOP);\n\t\twindow_lower(m->root);\n\t\tif (focus_follows_pointer) {\n\t\t\twindow_show(m->root);\n\t\t}\n\t} else {\n\t\twindow_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);\n\t\tput_status(SBSC_MASK_MONITOR_GEOMETRY, \"monitor_geometry 0x%08X %ux%u+%i+%i\\n\",\n\t\t           m->id, rect->width, rect->height, rect->x, rect->y);\n\t}\n\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\tif (n->client == NULL) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tadapt_geometry(&last_rect, rect, n);\n\t\t}\n\t\tarrange(m, d);\n\t}\n\treorder_monitor(m);\n}\n\nvoid reorder_monitor(monitor_t *m)\n{\n\tif (m == NULL) {\n\t\treturn;\n\t}\n\tmonitor_t *prev = m->prev;\n\twhile (prev != NULL && rect_cmp(m->rectangle, prev->rectangle) < 0) {\n\t\tswap_monitors(m, prev);\n\t\tprev = m->prev;\n\t}\n\tmonitor_t *next = m->next;\n\twhile (next != NULL && rect_cmp(m->rectangle, next->rectangle) > 0) {\n\t\tswap_monitors(m, next);\n\t\tnext = m->next;\n\t}\n}\n\nvoid rename_monitor(monitor_t *m, const char *name)\n{\n\tput_status(SBSC_MASK_MONITOR_RENAME, \"monitor_rename 0x%08X %s %s\\n\", m->id, m->name, name);\n\n\tsnprintf(m->name, sizeof(m->name), \"%s\", name);\n\txcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);\n\n\tput_status(SBSC_MASK_REPORT);\n}\n\nmonitor_t *find_monitor(uint32_t id)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (m->id == id) {\n\t\t\treturn m;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nmonitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (m->randr_id == id) {\n\t\t\treturn m;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid embrace_client(monitor_t *m, client_t *c)\n{\n\tif ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {\n\t\tc->floating_rectangle.x = m->rectangle.x;\n\t} else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {\n\t\tc->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;\n\t}\n\tif ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {\n\t\tc->floating_rectangle.y = m->rectangle.y;\n\t} else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {\n\t\tc->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;\n\t}\n}\n\nvoid adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)\n{\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\tclient_t *c = f->client;\n\t\t/* Clip the rectangle to fit into the monitor.\tWithout this, the fitting\n\t\t * algorithm doesn't work as expected. This also conserves the\n\t\t * out-of-bounds regions */\n\t\tint left_adjust = MAX((rs->x - c->floating_rectangle.x), 0);\n\t\tint top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);\n\t\tint right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);\n\t\tint bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->height), 0);\n\t\tc->floating_rectangle.x += left_adjust;\n\t\tc->floating_rectangle.y += top_adjust;\n\t\tc->floating_rectangle.width -= (left_adjust + right_adjust);\n\t\tc->floating_rectangle.height -= (top_adjust + bottom_adjust);\n\n\t\tint dx_s = c->floating_rectangle.x - rs->x;\n\t\tint dy_s = c->floating_rectangle.y - rs->y;\n\n\t\tint nume_x = dx_s * (rd->width - c->floating_rectangle.width);\n\t\tint nume_y = dy_s * (rd->height - c->floating_rectangle.height);\n\n\t\tint deno_x = rs->width - c->floating_rectangle.width;\n\t\tint deno_y = rs->height - c->floating_rectangle.height;\n\n\t\tint dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);\n\t\tint dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);\n\n\t\t/* Translate and undo clipping */\n\t\tc->floating_rectangle.width += left_adjust + right_adjust;\n\t\tc->floating_rectangle.height += top_adjust + bottom_adjust;\n\t\tc->floating_rectangle.x = rd->x + dx_d - left_adjust;\n\t\tc->floating_rectangle.y = rd->y + dy_d - top_adjust;\n\t}\n}\n\nvoid add_monitor(monitor_t *m)\n{\n\txcb_rectangle_t r = m->rectangle;\n\n\tif (mon == NULL) {\n\t\tmon = m;\n\t\tmon_head = m;\n\t\tmon_tail = m;\n\t} else {\n\t\tmonitor_t *a = mon_head;\n\t\twhile (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {\n\t\t\ta = a->next;\n\t\t}\n\t\tif (a != NULL) {\n\t\t\tmonitor_t *b = a->prev;\n\t\t\tif (b != NULL) {\n\t\t\t\tb->next = m;\n\t\t\t} else {\n\t\t\t\tmon_head = m;\n\t\t\t}\n\t\t\tm->prev = b;\n\t\t\tm->next = a;\n\t\t\ta->prev = m;\n\t\t} else {\n\t\t\tmon_tail->next = m;\n\t\t\tm->prev = mon_tail;\n\t\t\tmon_tail = m;\n\t\t}\n\t}\n\n\tput_status(SBSC_MASK_MONITOR_ADD, \"monitor_add 0x%08X %s %ux%u+%i+%i\\n\", m->id, m->name, r.width, r.height, r.x, r.y);\n\n\tput_status(SBSC_MASK_REPORT);\n}\n\nvoid unlink_monitor(monitor_t *m)\n{\n\tmonitor_t *prev = m->prev;\n\tmonitor_t *next = m->next;\n\n\tif (prev != NULL) {\n\t\tprev->next = next;\n\t}\n\n\tif (next != NULL) {\n\t\tnext->prev = prev;\n\t}\n\n\tif (mon_head == m) {\n\t\tmon_head = next;\n\t}\n\n\tif (mon_tail == m) {\n\t\tmon_tail = prev;\n\t}\n\n\tif (pri_mon == m) {\n\t\tpri_mon = NULL;\n\t}\n\n\tif (mon == m) {\n\t\tmon = NULL;\n\t}\n}\n\nvoid remove_monitor(monitor_t *m)\n{\n\tput_status(SBSC_MASK_MONITOR_REMOVE, \"monitor_remove 0x%08X\\n\", m->id);\n\n\twhile (m->desk_head != NULL) {\n\t\tremove_desktop(m, m->desk_head);\n\t}\n\n\tmonitor_t *last_mon = mon;\n\n\tunlink_monitor(m);\n\txcb_destroy_window(dpy, m->root);\n\tfree(m);\n\n\tif (mon != last_mon) {\n\t\tfocus_node(NULL, NULL, NULL);\n\t}\n\n\tput_status(SBSC_MASK_REPORT);\n}\n\nvoid merge_monitors(monitor_t *ms, monitor_t *md)\n{\n\tif (ms == NULL || md == NULL || ms == md) {\n\t\treturn;\n\t}\n\n\tdesktop_t *d = ms->desk_head;\n\twhile (d != NULL) {\n\t\tdesktop_t *next = d->next;\n\t\ttransfer_desktop(ms, md, d, false);\n\t\td = next;\n\t}\n}\n\nbool swap_monitors(monitor_t *m1, monitor_t *m2)\n{\n\tif (m1 == NULL || m2 == NULL || m1 == m2) {\n\t\treturn false;\n\t}\n\n\tput_status(SBSC_MASK_MONITOR_SWAP, \"monitor_swap 0x%08X 0x%08X\\n\", m1->id, m2->id);\n\n\tif (mon_head == m1) {\n\t\tmon_head = m2;\n\t} else if (mon_head == m2) {\n\t\tmon_head = m1;\n\t}\n\tif (mon_tail == m1) {\n\t\tmon_tail = m2;\n\t} else if (mon_tail == m2) {\n\t\tmon_tail = m1;\n\t}\n\n\tmonitor_t *p1 = m1->prev;\n\tmonitor_t *n1 = m1->next;\n\tmonitor_t *p2 = m2->prev;\n\tmonitor_t *n2 = m2->next;\n\n\tif (p1 != NULL && p1 != m2) {\n\t\tp1->next = m2;\n\t}\n\tif (n1 != NULL && n1 != m2) {\n\t\tn1->prev = m2;\n\t}\n\tif (p2 != NULL && p2 != m1) {\n\t\tp2->next = m1;\n\t}\n\tif (n2 != NULL && n2 != m1) {\n\t\tn2->prev = m1;\n\t}\n\n\tm1->prev = p2 == m1 ? m2 : p2;\n\tm1->next = n2 == m1 ? m2 : n2;\n\tm2->prev = p1 == m2 ? m1 : p1;\n\tm2->next = n1 == m2 ? m1 : n1;\n\n\tewmh_update_wm_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_current_desktop();\n\n\tput_status(SBSC_MASK_REPORT);\n\treturn true;\n}\n\nmonitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t *sel)\n{\n\tmonitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);\n\n\tif (f == NULL) {\n\t\tf = (dir == CYCLE_PREV ? mon_tail : mon_head);\n\t}\n\n\twhile (f != m) {\n\t\tcoordinates_t loc = {f, NULL, NULL};\n\t\tif (monitor_matches(&loc, &loc, sel)) {\n\t\t\treturn f;\n\t\t}\n\t\tf = (dir == CYCLE_PREV ? f->prev : f->next);\n\t\tif (f == NULL) {\n\t\t\tf = (dir == CYCLE_PREV ? mon_tail : mon_head);\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nbool is_inside_monitor(monitor_t *m, xcb_point_t pt)\n{\n\treturn is_inside(pt, m->rectangle);\n}\n\nmonitor_t *monitor_from_point(xcb_point_t pt)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (is_inside_monitor(m, pt)) {\n\t\t\treturn m;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nmonitor_t *monitor_from_client(client_t *c)\n{\n\tint16_t xc = c->floating_rectangle.x + c->floating_rectangle.width/2;\n\tint16_t yc = c->floating_rectangle.y + c->floating_rectangle.height/2;\n\txcb_point_t pt = {xc, yc};\n\tmonitor_t *nearest = monitor_from_point(pt);\n\tif (nearest == NULL) {\n\t\tint dmin = INT_MAX;\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\txcb_rectangle_t r = m->rectangle;\n\t\t\tint d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);\n\t\t\tif (d < dmin) {\n\t\t\t\tdmin = d;\n\t\t\t\tnearest = m;\n\t\t\t}\n\t\t}\n\t}\n\treturn nearest;\n}\n\nmonitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t *sel)\n{\n\tuint32_t dmin = UINT32_MAX;\n\tmonitor_t *nearest = NULL;\n\txcb_rectangle_t rect = m->rectangle;\n\tfor (monitor_t *f = mon_head; f != NULL; f = f->next) {\n\t\tcoordinates_t loc = {f, NULL, NULL};\n\t\txcb_rectangle_t r = f->rectangle;\n\t\tif (f == m ||\n\t\t    !monitor_matches(&loc, &loc, sel) ||\n\t\t    !on_dir_side(rect, r, dir)) {\n\t\t\tcontinue;\n\t\t}\n\t\tuint32_t d = boundary_distance(rect, r, dir);\n\t\tif (d < dmin) {\n\t\t\tdmin = d;\n\t\t\tnearest = f;\n\t\t}\n\t}\n\treturn nearest;\n}\n\nbool find_any_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tcoordinates_t loc = {m, NULL, NULL};\n\t\tif (monitor_matches(&loc, ref, sel)) {\n\t\t\t*dst = loc;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool update_monitors(void)\n{\n\txcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);\n\tif (sres == NULL) {\n\t\treturn false;\n\t}\n\n\tmonitor_t *last_wired = NULL;\n\n\tint len = xcb_randr_get_screen_resources_outputs_length(sres);\n\txcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);\n\n\txcb_randr_get_output_info_cookie_t cookies[len];\n\tfor (int i = 0; i < len; i++) {\n\t\tcookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);\n\t}\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tm->wired = false;\n\t}\n\n\tfor (int i = 0; i < len; i++) {\n\t\txcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);\n\t\tif (info != NULL) {\n\t\t\tif (info->crtc != XCB_NONE) {\n\t\t\t\txcb_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);\n\t\t\t\tif (cir != NULL) {\n\t\t\t\t\txcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};\n\t\t\t\t\tlast_wired = get_monitor_by_randr_id(outputs[i]);\n\t\t\t\t\tif (last_wired != NULL) {\n\t\t\t\t\t\tupdate_root(last_wired, &rect);\n\t\t\t\t\t\tlast_wired->wired = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchar *name = (char *) xcb_randr_get_output_info_name(info);\n\t\t\t\t\t\tsize_t len = (size_t) xcb_randr_get_output_info_name_length(info);\n\t\t\t\t\t\tchar *name_copy = copy_string(name, len);\n\t\t\t\t\t\tlast_wired = make_monitor(name_copy, &rect, XCB_NONE);\n\t\t\t\t\t\tfree(name_copy);\n\t\t\t\t\t\tlast_wired->randr_id = outputs[i];\n\t\t\t\t\t\tadd_monitor(last_wired);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfree(cir);\n\t\t\t} else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {\n\t\t\t\tmonitor_t *m = get_monitor_by_randr_id(outputs[i]);\n\t\t\t\tif (m != NULL) {\n\t\t\t\t\tm->wired = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfree(info);\n\t}\n\n\txcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);\n\tif (gpo != NULL) {\n\t\tpri_mon = get_monitor_by_randr_id(gpo->output);\n\t}\n\tfree(gpo);\n\n\t/* handle overlapping monitors */\n\tif (merge_overlapping_monitors) {\n\t\tmonitor_t *m = mon_head;\n\t\twhile (m != NULL) {\n\t\t\tmonitor_t *next = m->next;\n\t\t\tif (m->wired) {\n\t\t\t\tmonitor_t *mb = mon_head;\n\t\t\t\twhile (mb != NULL) {\n\t\t\t\t\tmonitor_t *mb_next = mb->next;\n\t\t\t\t\tif (m != mb && mb->wired && contains(m->rectangle, mb->rectangle)) {\n\t\t\t\t\t\tif (last_wired == mb) {\n\t\t\t\t\t\t\tlast_wired = m;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (next == mb) {\n\t\t\t\t\t\t\tnext = mb_next;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmerge_monitors(mb, m);\n\t\t\t\t\t\tremove_monitor(mb);\n\t\t\t\t\t}\n\t\t\t\t\tmb = mb_next;\n\t\t\t\t}\n\t\t\t}\n\t\t\tm = next;\n\t\t}\n\t}\n\n\t/* merge and remove disconnected monitors */\n\tif (remove_unplugged_monitors) {\n\t\tmonitor_t *m = mon_head;\n\t\twhile (m != NULL) {\n\t\t\tmonitor_t *next = m->next;\n\t\t\tif (!m->wired) {\n\t\t\t\tmerge_monitors(m, last_wired);\n\t\t\t\tremove_monitor(m);\n\t\t\t}\n\t\t\tm = next;\n\t\t}\n\t}\n\n\t/* add one desktop to each new monitor */\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (m->desk == NULL) {\n\t\t\tadd_desktop(m, make_desktop(NULL, XCB_NONE));\n\t\t}\n\t}\n\n\tif (!running && mon != NULL) {\n\t\tif (pri_mon != NULL) {\n\t\t\tmon = pri_mon;\n\t\t}\n\t\tcenter_pointer(mon->rectangle);\n\t\tewmh_update_current_desktop();\n\t}\n\n\tfree(sres);\n\n\treturn (mon != NULL);\n}\n"
  },
  {
    "path": "src/monitor.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_MONITOR_H\n#define BSPWM_MONITOR_H\n\n#define DEFAULT_MON_NAME     \"MONITOR\"\n\nmonitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id);\nvoid update_root(monitor_t *m, xcb_rectangle_t *rect);\nvoid reorder_monitor(monitor_t *m);\nvoid rename_monitor(monitor_t *m, const char *name);\nmonitor_t *find_monitor(uint32_t id);\nmonitor_t *get_monitor_by_randr_id(xcb_randr_output_t id);\nvoid embrace_client(monitor_t *m, client_t *c);\nvoid adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);\nvoid add_monitor(monitor_t *m);\nvoid unlink_monitor(monitor_t *m);\nvoid remove_monitor(monitor_t *m);\nvoid merge_monitors(monitor_t *ms, monitor_t *md);\nbool swap_monitors(monitor_t *m1, monitor_t *m2);\nmonitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t *sel);\nbool is_inside_monitor(monitor_t *m, xcb_point_t pt);\nmonitor_t *monitor_from_point(xcb_point_t pt);\nmonitor_t *monitor_from_client(client_t *c);\nmonitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t *sel);\nbool find_any_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel);\nbool update_monitors(void);\n\n#endif\n"
  },
  {
    "path": "src/parse.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n#include <errno.h>\n#include \"parse.h\"\n\nbool parse_bool(char *value, bool *b)\n{\n\tif (streq(\"true\", value) || streq(\"on\", value)) {\n\t\t*b = true;\n\t\treturn true;\n\t} else if (streq(\"false\", value) || streq(\"off\", value)) {\n\t\t*b = false;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_split_type(char *s, split_type_t *t)\n{\n\tif (streq(\"horizontal\", s)) {\n\t\t*t = TYPE_HORIZONTAL;\n\t\treturn true;\n\t} else if (streq(\"vertical\", s)) {\n\t\t*t = TYPE_VERTICAL;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_split_mode(char *s, split_mode_t *m)\n{\n\tif (streq(\"automatic\", s)) {\n\t\t*m = MODE_AUTOMATIC;\n\t\treturn true;\n\t} else if (streq(\"vertical\", s)) {\n\t\t*m = MODE_MANUAL;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_layout(char *s, layout_t *l)\n{\n\tif (streq(\"monocle\", s)) {\n\t\t*l = LAYOUT_MONOCLE;\n\t\treturn true;\n\t} else if (streq(\"tiled\", s)) {\n\t\t*l = LAYOUT_TILED;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_client_state(char *s, client_state_t *t)\n{\n\tif (streq(\"tiled\", s)) {\n\t\t*t = STATE_TILED;\n\t\treturn true;\n\t} else if (streq(\"pseudo_tiled\", s)) {\n\t\t*t = STATE_PSEUDO_TILED;\n\t\treturn true;\n\t} else if (streq(\"floating\", s)) {\n\t\t*t = STATE_FLOATING;\n\t\treturn true;\n\t} else if (streq(\"fullscreen\", s)) {\n\t\t*t = STATE_FULLSCREEN;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_stack_layer(char *s, stack_layer_t *l)\n{\n\tif (streq(\"below\", s)) {\n\t\t*l = LAYER_BELOW;\n\t\treturn true;\n\t} else if (streq(\"normal\", s)) {\n\t\t*l = LAYER_NORMAL;\n\t\treturn true;\n\t} else if (streq(\"above\", s)) {\n\t\t*l = LAYER_ABOVE;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_direction(char *s, direction_t *d)\n{\n\tif (streq(\"north\", s)) {\n\t\t*d = DIR_NORTH;\n\t\treturn true;\n\t} else if (streq(\"west\", s)) {\n\t\t*d = DIR_WEST;\n\t\treturn true;\n\t} else if (streq(\"south\", s)) {\n\t\t*d = DIR_SOUTH;\n\t\treturn true;\n\t} else if (streq(\"east\", s)) {\n\t\t*d = DIR_EAST;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_cycle_direction(char *s, cycle_dir_t *d)\n{\n\tif (streq(\"next\", s)) {\n\t\t*d = CYCLE_NEXT;\n\t\treturn true;\n\t} else if (streq(\"prev\", s)) {\n\t\t*d = CYCLE_PREV;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_circulate_direction(char *s, circulate_dir_t *d)\n{\n\tif (streq(\"forward\", s)) {\n\t\t*d = CIRCULATE_FORWARD;\n\t\treturn true;\n\t} else if (streq(\"backward\", s)) {\n\t\t*d = CIRCULATE_BACKWARD;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_history_direction(char *s, history_dir_t *d)\n{\n\tif (streq(\"older\", s)) {\n\t\t*d = HISTORY_OLDER;\n\t\treturn true;\n\t} else if (streq(\"newer\", s)) {\n\t\t*d = HISTORY_NEWER;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\nbool parse_flip(char *s, flip_t *f)\n{\n\tif (streq(\"horizontal\", s)) {\n\t\t*f = FLIP_HORIZONTAL;\n\t\treturn true;\n\t} else if (streq(\"vertical\", s)) {\n\t\t*f = FLIP_VERTICAL;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_resize_handle(char *s, resize_handle_t *h)\n{\n\tif (streq(\"left\", s)) {\n\t\t*h = HANDLE_LEFT;\n\t\treturn true;\n\t} else if (streq(\"top\", s)) {\n\t\t*h = HANDLE_TOP;\n\t\treturn true;\n\t} else if (streq(\"right\", s)) {\n\t\t*h = HANDLE_RIGHT;\n\t\treturn true;\n\t} else if (streq(\"bottom\", s)) {\n\t\t*h = HANDLE_BOTTOM;\n\t\treturn true;\n\t} else if (streq(\"top_left\", s)) {\n\t\t*h = HANDLE_TOP_LEFT;\n\t\treturn true;\n\t} else if (streq(\"top_right\", s)) {\n\t\t*h = HANDLE_TOP_RIGHT;\n\t\treturn true;\n\t} else if (streq(\"bottom_right\", s)) {\n\t\t*h = HANDLE_BOTTOM_RIGHT;\n\t\treturn true;\n\t} else if (streq(\"bottom_left\", s)) {\n\t\t*h = HANDLE_BOTTOM_LEFT;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_modifier_mask(char *s, uint16_t *m)\n{\n\tif (strcmp(s, \"shift\") == 0) {\n\t\t*m = XCB_MOD_MASK_SHIFT;\n\t\treturn true;\n\t} else if (strcmp(s, \"control\") == 0) {\n\t\t*m = XCB_MOD_MASK_CONTROL;\n\t\treturn true;\n\t} else if (strcmp(s, \"lock\") == 0) {\n\t\t*m = XCB_MOD_MASK_LOCK;\n\t\treturn true;\n\t} else if (strcmp(s, \"mod1\") == 0) {\n\t\t*m = XCB_MOD_MASK_1;\n\t\treturn true;\n\t} else if (strcmp(s, \"mod2\") == 0) {\n\t\t*m = XCB_MOD_MASK_2;\n\t\treturn true;\n\t} else if (strcmp(s, \"mod3\") == 0) {\n\t\t*m = XCB_MOD_MASK_3;\n\t\treturn true;\n\t} else if (strcmp(s, \"mod4\") == 0) {\n\t\t*m = XCB_MOD_MASK_4;\n\t\treturn true;\n\t} else if (strcmp(s, \"mod5\") == 0) {\n\t\t*m = XCB_MOD_MASK_5;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_button_index(char *s, int8_t *b)\n{\n\tif (strcmp(s, \"any\") == 0) {\n\t\t*b = XCB_BUTTON_INDEX_ANY;\n\t\treturn true;\n\t} else if (strcmp(s, \"button1\") == 0) {\n\t\t*b = XCB_BUTTON_INDEX_1;\n\t\treturn true;\n\t} else if (strcmp(s, \"button2\") == 0) {\n\t\t*b = XCB_BUTTON_INDEX_2;\n\t\treturn true;\n\t} else if (strcmp(s, \"button3\") == 0) {\n\t\t*b = XCB_BUTTON_INDEX_3;\n\t\treturn true;\n\t} else if (strcmp(s, \"none\") == 0) {\n\t\t*b = -1;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_pointer_action(char *s, pointer_action_t *a)\n{\n\tif (streq(\"move\", s)) {\n\t\t*a = ACTION_MOVE;\n\t\treturn true;\n\t} else if (streq(\"resize_corner\", s)) {\n\t\t*a = ACTION_RESIZE_CORNER;\n\t\treturn true;\n\t} else if (streq(\"resize_side\", s)) {\n\t\t*a = ACTION_RESIZE_SIDE;\n\t\treturn true;\n\t} else if (streq(\"focus\", s)) {\n\t\t*a = ACTION_FOCUS;\n\t\treturn true;\n\t} else if (streq(\"none\", s)) {\n\t\t*a = ACTION_NONE;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_child_polarity(char *s, child_polarity_t *p)\n{\n\tif (streq(\"first_child\", s)) {\n\t\t*p = FIRST_CHILD;\n\t\treturn true;\n\t} else if (streq(\"second_child\", s)) {\n\t\t*p = SECOND_CHILD;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_automatic_scheme(char *s, automatic_scheme_t *a)\n{\n\tif (streq(\"longest_side\", s)) {\n\t\t*a = SCHEME_LONGEST_SIDE;\n\t\treturn true;\n\t} else if (streq(\"alternate\", s)) {\n\t\t*a = SCHEME_ALTERNATE;\n\t\treturn true;\n\t} else if (streq(\"spiral\", s)) {\n\t\t*a = SCHEME_SPIRAL;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_honor_size_hints_mode(char *s, honor_size_hints_mode_t *a)\n{\n\tbool b;\n\tif (parse_bool(s, &b)) {\n\t\t*a = b ? HONOR_SIZE_HINTS_YES : HONOR_SIZE_HINTS_NO;\n\t\treturn true;\n\t} else if (streq(\"floating\", s)) {\n\t\t*a = HONOR_SIZE_HINTS_FLOATING;\n\t\treturn true;\n\t} else if (streq(\"tiled\", s)) {\n\t\t*a = HONOR_SIZE_HINTS_TILED;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_state_transition(char *s, state_transition_t *m)\n{\n\tif (streq(\"none\", s)) {\n\t\t*m = 0;\n\t\treturn true;\n\t} else if (streq(\"all\", s)) {\n\t\t*m = STATE_TRANSITION_ENTER | STATE_TRANSITION_EXIT;\n\t\treturn true;\n\t} else {\n\t\tstate_transition_t w = 0;\n\t\tchar *x = copy_string(s, strlen(s));\n\t\tchar *key = strtok(x, \",\");\n\t\twhile (key != NULL) {\n\t\t\tif (streq(\"enter\", key)) {\n\t\t\t\tw |= STATE_TRANSITION_ENTER;\n\t\t\t} else if (streq(\"exit\", key)) {\n\t\t\t\tw |= STATE_TRANSITION_EXIT;\n\t\t\t} else {\n\t\t\t\tfree(x);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tkey = strtok(NULL, \",\");\n\t\t}\n\t\tfree(x);\n\t\tif (w != 0) {\n\t\t\t*m = w;\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool parse_tightness(char *s, tightness_t *t)\n{\n\tif (streq(\"high\", s)) {\n\t\t*t = TIGHTNESS_HIGH;\n\t\treturn true;\n\t} else if (streq(\"low\", s)) {\n\t\t*t = TIGHTNESS_LOW;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool parse_degree(char *s, int *d)\n{\n\tint i = atoi(s);\n\twhile (i < 0)\n\t\ti += 360;\n\twhile (i > 359)\n\t\ti -= 360;\n\tif ((i % 90) != 0) {\n\t\treturn false;\n\t} else {\n\t\t*d = i;\n\t\treturn true;\n\t}\n}\n\nbool parse_id(char *s, uint32_t *id)\n{\n\tchar *end;\n\terrno = 0;\n\tuint32_t v = strtol(s, &end, 0);\n\tif (errno != 0 || *end != '\\0') {\n\t\treturn false;\n\t}\n\t*id = v;\n\treturn true;\n}\n\nbool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)\n{\n\t*key = strtok(s, EQL_TOK);\n\tchar *v = strtok(NULL, EQL_TOK);\n\tif (v == NULL) {\n\t\t*state = ALTER_TOGGLE;\n\t\treturn true;\n\t} else {\n\t\tif (parse_bool(v, value)) {\n\t\t\t*state = ALTER_SET;\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool parse_index(char *s, uint16_t *idx)\n{\n\treturn (sscanf(s, \"^%hu\", idx) == 1);\n}\n\nbool parse_rectangle(char *s, xcb_rectangle_t *r)\n{\n\tuint16_t w, h;\n\tint16_t x, y;\n\tif (sscanf(s, \"%hux%hu+%hi+%hi\", &w, &h, &x, &y) != 4) {\n\t\treturn false;\n\t}\n\tr->width = w;\n\tr->height = h;\n\tr->x = x;\n\tr->y = y;\n\treturn true;\n}\n\nbool parse_subscriber_mask(char *s, subscriber_mask_t *mask)\n{\n\tif (streq(\"all\", s)) {\n\t\t*mask = SBSC_MASK_ALL;\n\t} else if (streq(\"node\", s)) {\n\t\t*mask = SBSC_MASK_NODE;\n\t} else if (streq(\"desktop\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP;\n\t} else if (streq(\"monitor\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR;\n\t} else if (streq(\"pointer_action\", s)) {\n\t\t*mask = SBSC_MASK_POINTER_ACTION;\n\t} else if (streq(\"node_add\", s)) {\n\t\t*mask = SBSC_MASK_NODE_ADD;\n\t} else if (streq(\"node_remove\", s)) {\n\t\t*mask = SBSC_MASK_NODE_REMOVE;\n\t} else if (streq(\"node_swap\", s)) {\n\t\t*mask = SBSC_MASK_NODE_SWAP;\n\t} else if (streq(\"node_transfer\", s)) {\n\t\t*mask = SBSC_MASK_NODE_TRANSFER;\n\t} else if (streq(\"node_focus\", s)) {\n\t\t*mask = SBSC_MASK_NODE_FOCUS;\n\t} else if (streq(\"node_presel\", s)) {\n\t\t*mask = SBSC_MASK_NODE_PRESEL;\n\t} else if (streq(\"node_stack\", s)) {\n\t\t*mask = SBSC_MASK_NODE_STACK;\n\t} else if (streq(\"node_activate\", s)) {\n\t\t*mask = SBSC_MASK_NODE_ACTIVATE;\n\t} else if (streq(\"node_geometry\", s)) {\n\t\t*mask = SBSC_MASK_NODE_GEOMETRY;\n\t} else if (streq(\"node_state\", s)) {\n\t\t*mask = SBSC_MASK_NODE_STATE;\n\t} else if (streq(\"node_flag\", s)) {\n\t\t*mask = SBSC_MASK_NODE_FLAG;\n\t} else if (streq(\"node_layer\", s)) {\n\t\t*mask = SBSC_MASK_NODE_LAYER;\n\t} else if (streq(\"desktop_add\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_ADD;\n\t} else if (streq(\"desktop_rename\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_RENAME;\n\t} else if (streq(\"desktop_remove\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_REMOVE;\n\t} else if (streq(\"desktop_swap\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_SWAP;\n\t} else if (streq(\"desktop_transfer\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_TRANSFER;\n\t} else if (streq(\"desktop_focus\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_FOCUS;\n\t} else if (streq(\"desktop_activate\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_ACTIVATE;\n\t} else if (streq(\"desktop_layout\", s)) {\n\t\t*mask = SBSC_MASK_DESKTOP_LAYOUT;\n\t} else if (streq(\"monitor_add\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_ADD;\n\t} else if (streq(\"monitor_rename\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_RENAME;\n\t} else if (streq(\"monitor_remove\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_REMOVE;\n\t} else if (streq(\"monitor_swap\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_SWAP;\n\t} else if (streq(\"monitor_focus\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_FOCUS;\n\t} else if (streq(\"monitor_geometry\", s)) {\n\t\t*mask = SBSC_MASK_MONITOR_GEOMETRY;\n\t} else if (streq(\"report\", s)) {\n\t\t*mask = SBSC_MASK_REPORT;\n\t} else {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\n#define GET_MOD(k) \\\n\t} else if (streq(#k, tok)) { \\\n\t\tsel->k = OPTION_TRUE; \\\n\t} else if (streq(\"!\" #k, tok)) { \\\n\t\tsel->k = OPTION_FALSE;\n\nbool parse_monitor_modifiers(char *desc, monitor_select_t *sel)\n{\n\tchar *tok;\n\twhile ((tok = strrchr(desc, CAT_CHR)) != NULL) {\n\t\ttok[0] = '\\0';\n\t\ttok++;\n\t\tif (streq(\"occupied\", tok)) {\n\t\t\tsel->occupied = OPTION_TRUE;\n\t\t} else if (streq(\"!occupied\", tok)) {\n\t\t\tsel->occupied = OPTION_FALSE;\n\t\tGET_MOD(focused)\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nbool parse_desktop_modifiers(char *desc, desktop_select_t *sel)\n{\n\tchar *tok;\n\twhile ((tok = strrchr(desc, CAT_CHR)) != NULL) {\n\t\ttok[0] = '\\0';\n\t\ttok++;\n\t\tif (streq(\"occupied\", tok)) {\n\t\t\tsel->occupied = OPTION_TRUE;\n\t\t} else if (streq(\"!occupied\", tok)) {\n\t\t\tsel->occupied = OPTION_FALSE;\n\t\tGET_MOD(focused)\n\t\tGET_MOD(active)\n\t\tGET_MOD(urgent)\n\t\tGET_MOD(local)\n\t\tGET_MOD(tiled)\n\t\tGET_MOD(monocle)\n\t\tGET_MOD(user_tiled)\n\t\tGET_MOD(user_monocle)\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n\n}\n\nbool parse_node_modifiers(char *desc, node_select_t *sel)\n{\n\tchar *tok;\n\twhile ((tok = strrchr(desc, CAT_CHR)) != NULL) {\n\t\ttok[0] = '\\0';\n\t\ttok++;\n\t\tif (streq(\"tiled\", tok)) {\n\t\t\tsel->tiled = OPTION_TRUE;\n\t\t} else if (streq(\"!tiled\", tok)) {\n\t\t\tsel->tiled = OPTION_FALSE;\n\t\tGET_MOD(automatic)\n\t\tGET_MOD(focused)\n\t\tGET_MOD(active)\n\t\tGET_MOD(local)\n\t\tGET_MOD(leaf)\n\t\tGET_MOD(window)\n\t\tGET_MOD(pseudo_tiled)\n\t\tGET_MOD(floating)\n\t\tGET_MOD(fullscreen)\n\t\tGET_MOD(hidden)\n\t\tGET_MOD(sticky)\n\t\tGET_MOD(private)\n\t\tGET_MOD(locked)\n\t\tGET_MOD(marked)\n\t\tGET_MOD(urgent)\n\t\tGET_MOD(same_class)\n\t\tGET_MOD(descendant_of)\n\t\tGET_MOD(ancestor_of)\n\t\tGET_MOD(below)\n\t\tGET_MOD(normal)\n\t\tGET_MOD(above)\n\t\tGET_MOD(horizontal)\n\t\tGET_MOD(vertical)\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n#undef GET_MOD\n"
  },
  {
    "path": "src/parse.h",
    "content": "#ifndef BSPWM_PARSE_H\n#define BSPWM_PARSE_H\n\n#include \"types.h\"\n#include \"subscribe.h\"\n\n#define OPT_CHR  '-'\n#define CAT_CHR  '.'\n#define EQL_TOK  \"=\"\n#define COL_TOK  \":\"\n\nbool parse_bool(char *value, bool *b);\nbool parse_split_type(char *s, split_type_t *t);\nbool parse_split_mode(char *s, split_mode_t *m);\nbool parse_layout(char *s, layout_t *l);\nbool parse_client_state(char *s, client_state_t *t);\nbool parse_stack_layer(char *s, stack_layer_t *l);\nbool parse_direction(char *s, direction_t *d);\nbool parse_cycle_direction(char *s, cycle_dir_t *d);\nbool parse_circulate_direction(char *s, circulate_dir_t *d);\nbool parse_history_direction(char *s, history_dir_t *d);\nbool parse_flip(char *s, flip_t *f);\nbool parse_resize_handle(char *s, resize_handle_t *h);\nbool parse_modifier_mask(char *s, uint16_t *m);\nbool parse_button_index(char *s, int8_t *b);\nbool parse_pointer_action(char *s, pointer_action_t *a);\nbool parse_child_polarity(char *s, child_polarity_t *p);\nbool parse_automatic_scheme(char *s, automatic_scheme_t *a);\nbool parse_honor_size_hints_mode(char *s, honor_size_hints_mode_t *a);\nbool parse_state_transition(char *s, state_transition_t *m);\nbool parse_tightness(char *s, tightness_t *t);\nbool parse_degree(char *s, int *d);\nbool parse_id(char *s, uint32_t *id);\nbool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);\nbool parse_index(char *s, uint16_t *idx);\nbool parse_rectangle(char *s, xcb_rectangle_t *r);\nbool parse_subscriber_mask(char *s, subscriber_mask_t *mask);\nbool parse_monitor_modifiers(char *desc, monitor_select_t *sel);\nbool parse_desktop_modifiers(char *desc, desktop_select_t *sel);\nbool parse_node_modifiers(char *desc, node_select_t *sel);\n\n#endif\n"
  },
  {
    "path": "src/pointer.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <xcb/xcb_keysyms.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include \"bspwm.h\"\n#include \"query.h\"\n#include \"settings.h\"\n#include \"stack.h\"\n#include \"tree.h\"\n#include \"monitor.h\"\n#include \"subscribe.h\"\n#include \"events.h\"\n#include \"window.h\"\n#include \"pointer.h\"\n\nuint16_t num_lock;\nuint16_t caps_lock;\nuint16_t scroll_lock;\n\nbool grabbing;\nnode_t *grabbed_node;\n\nvoid pointer_init(void)\n{\n\tnum_lock = modfield_from_keysym(XK_Num_Lock);\n\tcaps_lock = modfield_from_keysym(XK_Caps_Lock);\n\tscroll_lock = modfield_from_keysym(XK_Scroll_Lock);\n\tif (caps_lock == XCB_NO_SYMBOL) {\n\t\tcaps_lock = XCB_MOD_MASK_LOCK;\n\t}\n\tgrabbing = false;\n\tgrabbed_node = NULL;\n}\n\nvoid window_grab_buttons(xcb_window_t win)\n{\n\tfor (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {\n\t\tif (click_to_focus == (int8_t) XCB_BUTTON_INDEX_ANY || click_to_focus == (int8_t) BUTTONS[i]) {\n\t\t\twindow_grab_button(win, BUTTONS[i], XCB_NONE);\n\t\t}\n\t\tif (pointer_actions[i] != ACTION_NONE) {\n\t\t\twindow_grab_button(win, BUTTONS[i], pointer_modifier);\n\t\t}\n\t}\n}\n\nvoid window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)\n{\n#define GRAB(b, m) \\\n\txcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \\\n\t                XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)\n\t\tGRAB(button, modifier);\n\t\tif (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | num_lock | caps_lock | scroll_lock);\n\t\t}\n\t\tif (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | num_lock | caps_lock);\n\t\t}\n\t\tif (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | caps_lock | scroll_lock);\n\t\t}\n\t\tif (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | num_lock | scroll_lock);\n\t\t}\n\t\tif (num_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | num_lock);\n\t\t}\n\t\tif (caps_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | caps_lock);\n\t\t}\n\t\tif (scroll_lock != XCB_NO_SYMBOL) {\n\t\t\tGRAB(button, modifier | scroll_lock);\n\t\t}\n#undef GRAB\n}\n\nvoid grab_buttons(void)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\twindow_grab_buttons(n->id);\n\t\t\t\tif (n->presel != NULL) {\n\t\t\t\t\twindow_grab_buttons(n->presel->feedback);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid ungrab_buttons(void)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\txcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);\n\t\t\t}\n\t\t}\n\t}\n}\n\nint16_t modfield_from_keysym(xcb_keysym_t keysym)\n{\n\tuint16_t modfield = 0;\n\txcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;\n\txcb_get_modifier_mapping_reply_t *reply = NULL;\n\txcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);\n\n\tif ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||\n\t    (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||\n\t    reply->keycodes_per_modifier < 1 ||\n\t    (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {\n\t\tgoto end;\n\t}\n\n\tunsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;\n\tfor (unsigned int i = 0; i < num_mod; i++) {\n\t\tfor (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {\n\t\t\txcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];\n\t\t\tif (mk == XCB_NO_SYMBOL) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {\n\t\t\t\tif (*k == mk) {\n\t\t\t\t\tmodfield |= (1 << i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nend:\n\txcb_key_symbols_free(symbols);\n\tfree(keycodes);\n\tfree(reply);\n\treturn modfield;\n}\n\nresize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)\n{\n\tresize_handle_t rh = HANDLE_BOTTOM_RIGHT;\n\txcb_rectangle_t rect = get_rectangle(NULL, NULL, n);\n\tif (pac == ACTION_RESIZE_SIDE) {\n\t\tfloat W = rect.width;\n\t\tfloat H = rect.height;\n\t\tfloat ratio = W / H;\n\t\tfloat x = pos.x - rect.x;\n\t\tfloat y = pos.y - rect.y;\n\t\tfloat diag_a = ratio * y;\n\t\tfloat diag_b = W - diag_a;\n\t\tif (x < diag_a) {\n\t\t\tif (x < diag_b) {\n\t\t\t\trh = HANDLE_LEFT;\n\t\t\t} else {\n\t\t\t\trh = HANDLE_BOTTOM;\n\t\t\t}\n\t\t} else {\n\t\t\tif (x < diag_b) {\n\t\t\t\trh = HANDLE_TOP;\n\t\t\t} else {\n\t\t\t\trh = HANDLE_RIGHT;\n\t\t\t}\n\t\t}\n\t} else if (pac == ACTION_RESIZE_CORNER) {\n\t\tint16_t mid_x = rect.x + (rect.width / 2);\n\t\tint16_t mid_y = rect.y + (rect.height / 2);\n\t\tif (pos.x > mid_x) {\n\t\t\tif (pos.y > mid_y) {\n\t\t\t\trh = HANDLE_BOTTOM_RIGHT;\n\t\t\t} else {\n\t\t\t\trh = HANDLE_TOP_RIGHT;\n\t\t\t}\n\t\t} else {\n\t\t\tif (pos.y > mid_y) {\n\t\t\t\trh = HANDLE_BOTTOM_LEFT;\n\t\t\t} else {\n\t\t\t\trh = HANDLE_TOP_LEFT;\n\t\t\t}\n\t\t}\n\t}\n\treturn rh;\n}\n\nbool grab_pointer(pointer_action_t pac)\n{\n\txcb_window_t win = XCB_NONE;\n\txcb_point_t pos;\n\n\tquery_pointer(&win, &pos);\n\n\tcoordinates_t loc;\n\n\tif (!locate_window(win, &loc)) {\n\t\tif (pac == ACTION_FOCUS) {\n\t\t\tmonitor_t *m = monitor_from_point(pos);\n\t\t\tif (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {\n\t\t\t\tfocus_node(m, m->desk, m->desk->focus);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (pac == ACTION_FOCUS) {\n\t\tif (loc.node != mon->desk->focus) {\n\t\t\tfocus_node(loc.monitor, loc.desktop, loc.node);\n\t\t\treturn true;\n\t\t} else if (focus_follows_pointer) {\n\t\t\tstack(loc.desktop, loc.node, true);\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (loc.node->client->state == STATE_FULLSCREEN) {\n\t\treturn true;\n\t}\n\n\txcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);\n\n\tif (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {\n\t\tfree(reply);\n\t\treturn true;\n\t}\n\tfree(reply);\n\n\tif (pac == ACTION_MOVE) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X move begin\\n\", loc.monitor->id, loc.desktop->id, loc.node->id);\n\t} else if (pac == ACTION_RESIZE_CORNER) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\\n\", loc.monitor->id, loc.desktop->id, loc.node->id);\n\t} else if (pac == ACTION_RESIZE_SIDE) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\\n\", loc.monitor->id, loc.desktop->id, loc.node->id);\n\t}\n\n\ttrack_pointer(loc, pac, pos);\n\n\treturn true;\n}\n\nvoid track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)\n{\n\tnode_t *n = loc.node;\n\tresize_handle_t rh = get_handle(loc.node, pos, pac);\n\n\tuint16_t last_motion_x = pos.x, last_motion_y = pos.y;\n\txcb_timestamp_t last_motion_time = 0;\n\n\txcb_generic_event_t *evt = NULL;\n\n\tgrabbing = true;\n\tgrabbed_node = n;\n\n\tdo {\n\t\tfree(evt);\n\t\twhile ((evt = xcb_wait_for_event(dpy)) == NULL) {\n\t\t\txcb_flush(dpy);\n\t\t}\n\t\tuint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);\n\t\tif (resp_type == XCB_MOTION_NOTIFY) {\n\t\t\txcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;\n\t\t\tuint32_t dtime = e->time - last_motion_time;\n\t\t\tif (dtime < pointer_motion_interval) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlast_motion_time = e->time;\n\t\t\tint16_t dx = e->root_x - last_motion_x;\n\t\t\tint16_t dy = e->root_y - last_motion_y;\n\t\t\tif (pac == ACTION_MOVE) {\n\t\t\t\tmove_client(&loc, dx, dy);\n\t\t\t} else if (n) {\n\t\t\t\tclient_t *c = n->client;\n\t\t\t\tif (c && SHOULD_HONOR_SIZE_HINTS(c->honor_size_hints, c->state)) {\n\t\t\t\t\tresize_client(&loc, rh, e->root_x, e->root_y, false);\n\t\t\t\t} else {\n\t\t\t\t\tresize_client(&loc, rh, dx, dy, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlast_motion_x = e->root_x;\n\t\t\tlast_motion_y = e->root_y;\n\t\t\txcb_flush(dpy);\n\t\t} else if (resp_type == XCB_BUTTON_RELEASE) {\n\t\t\tgrabbing = false;\n\t\t} else {\n\t\t\thandle_event(evt);\n\t\t}\n\t} while (grabbing && grabbed_node != NULL);\n\tfree(evt);\n\n\txcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);\n\n\tif (grabbed_node == NULL) {\n\t\tgrabbing = false;\n\t\treturn;\n\t}\n\n\tif (pac == ACTION_MOVE) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X move end\\n\", loc.monitor->id, loc.desktop->id, n->id);\n\t} else if (pac == ACTION_RESIZE_CORNER) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\\n\", loc.monitor->id, loc.desktop->id, n->id);\n\t} else if (pac == ACTION_RESIZE_SIDE) {\n\t\tput_status(SBSC_MASK_POINTER_ACTION, \"pointer_action 0x%08X 0x%08X 0x%08X resize_side end\\n\", loc.monitor->id, loc.desktop->id, n->id);\n\t}\n\n\txcb_rectangle_t r = get_rectangle(NULL, NULL, n);\n\n\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);\n\n\tif ((pac == ACTION_MOVE && IS_TILED(n->client)) ||\n\t    ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&\n\t     n->client->state == STATE_TILED)) {\n\t\tfor (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {\n\t\t\tif (f == n || f->client == NULL || !IS_TILED(f->client)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\txcb_rectangle_t r = f->client->tiled_rectangle;\n\t\t\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/pointer.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_POINTER_H\n#define BSPWM_POINTER_H\n\n#define XK_Num_Lock     0xff7f\n#define XK_Caps_Lock    0xffe5\n#define XK_Scroll_Lock  0xff14\n\nextern uint16_t num_lock;\nextern uint16_t caps_lock;\nextern uint16_t scroll_lock;\n\nextern bool grabbing;\nextern node_t *grabbed_node;\n\nvoid pointer_init(void);\nvoid window_grab_buttons(xcb_window_t win);\nvoid window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier);\nvoid grab_buttons(void);\nvoid ungrab_buttons(void);\nint16_t modfield_from_keysym(xcb_keysym_t keysym);\nresize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac);\nbool grab_pointer(pointer_action_t pac);\nvoid track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);\n\n#endif\n"
  },
  {
    "path": "src/query.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"history.h\"\n#include \"parse.h\"\n#include \"monitor.h\"\n#include \"window.h\"\n#include \"tree.h\"\n#include \"query.h\"\n#include \"geometry.h\"\n\nvoid query_state(FILE *rsp)\n{\n\tfprintf(rsp, \"{\");\n\tfprintf(rsp, \"\\\"focusedMonitorId\\\":%u,\", mon->id);\n\tif (pri_mon != NULL) {\n\t\tfprintf(rsp, \"\\\"primaryMonitorId\\\":%u,\", pri_mon->id);\n\t}\n\tfprintf(rsp, \"\\\"clientsCount\\\":%i,\", clients_count);\n\tfprintf(rsp, \"\\\"monitors\\\":\");\n\tfprintf(rsp, \"[\");\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tquery_monitor(m, rsp);\n\t\tif (m->next != NULL) {\n\t\t\tfprintf(rsp, \",\");\n\t\t}\n\t}\n\tfprintf(rsp, \"]\");\n\tfprintf(rsp,\",\");\n\tfprintf(rsp, \"\\\"focusHistory\\\":\");\n\tquery_history(rsp);\n\tfprintf(rsp,\",\");\n\tfprintf(rsp, \"\\\"stackingList\\\":\");\n\tquery_stack(rsp);\n\tif (restart) {\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"eventSubscribers\\\":\");\n\t\tquery_subscribers(rsp);\n\t}\n\tfprintf(rsp, \"}\");\n}\n\nvoid query_monitor(monitor_t *m, FILE *rsp)\n{\n\tfprintf(rsp, \"{\");\n\tfprintf(rsp, \"\\\"name\\\":\\\"%s\\\",\", m->name);\n\tfprintf(rsp, \"\\\"id\\\":%u,\", m->id);\n\tfprintf(rsp, \"\\\"randrId\\\":%u,\", m->randr_id);\n\tfprintf(rsp, \"\\\"wired\\\":%s,\", BOOL_STR(m->wired));\n\tfprintf(rsp, \"\\\"stickyCount\\\":%i,\", m->sticky_count);\n\tfprintf(rsp, \"\\\"windowGap\\\":%i,\", m->window_gap);\n\tfprintf(rsp, \"\\\"borderWidth\\\":%u,\", m->border_width);\n\tfprintf(rsp, \"\\\"focusedDesktopId\\\":%u,\", m->desk->id);\n\tfprintf(rsp, \"\\\"padding\\\":\");\n\tquery_padding(m->padding, rsp);\n\tfprintf(rsp,\",\");\n\tfprintf(rsp, \"\\\"rectangle\\\":\");\n\tquery_rectangle(m->rectangle, rsp);\n\tfprintf(rsp,\",\");\n\tfprintf(rsp, \"\\\"desktops\\\":\");\n\tfprintf(rsp, \"[\");\n\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\tquery_desktop(d, rsp);\n\t\tif (d->next != NULL) {\n\t\t\tfprintf(rsp,\",\");\n\t\t}\n\t}\n\tfprintf(rsp, \"]\");\n\tfprintf(rsp, \"}\");\n}\n\nvoid query_desktop(desktop_t *d, FILE *rsp)\n{\n\tfprintf(rsp, \"{\");\n\tfprintf(rsp, \"\\\"name\\\":\\\"%s\\\",\", d->name);\n\tfprintf(rsp, \"\\\"id\\\":%u,\", d->id);\n\tfprintf(rsp, \"\\\"layout\\\":\\\"%s\\\",\", LAYOUT_STR(d->layout));\n\tfprintf(rsp, \"\\\"userLayout\\\":\\\"%s\\\",\", LAYOUT_STR(d->user_layout));\n\tfprintf(rsp, \"\\\"windowGap\\\":%i,\", d->window_gap);\n\tfprintf(rsp, \"\\\"borderWidth\\\":%u,\", d->border_width);\n\tfprintf(rsp, \"\\\"focusedNodeId\\\":%u,\", d->focus != NULL ? d->focus->id : 0);\n\tfprintf(rsp, \"\\\"padding\\\":\");\n\tquery_padding(d->padding, rsp);\n\tfprintf(rsp,\",\");\n\tfprintf(rsp, \"\\\"root\\\":\");\n\tquery_node(d->root, rsp);\n\tfprintf(rsp, \"}\");\n}\n\nvoid query_node(node_t *n, FILE *rsp)\n{\n\tif (n == NULL) {\n\t\tfprintf(rsp, \"null\");\n\t} else {\n\t\tfprintf(rsp, \"{\");\n\t\tfprintf(rsp, \"\\\"id\\\":%u,\", n->id);\n\t\tfprintf(rsp, \"\\\"splitType\\\":\\\"%s\\\",\", SPLIT_TYPE_STR(n->split_type));\n\t\tfprintf(rsp, \"\\\"splitRatio\\\":%lf,\", n->split_ratio);\n\t\tfprintf(rsp, \"\\\"vacant\\\":%s,\", BOOL_STR(n->vacant));\n\t\tfprintf(rsp, \"\\\"hidden\\\":%s,\", BOOL_STR(n->hidden));\n\t\tfprintf(rsp, \"\\\"sticky\\\":%s,\", BOOL_STR(n->sticky));\n\t\tfprintf(rsp, \"\\\"private\\\":%s,\", BOOL_STR(n->private));\n\t\tfprintf(rsp, \"\\\"locked\\\":%s,\", BOOL_STR(n->locked));\n\t\tfprintf(rsp, \"\\\"marked\\\":%s,\", BOOL_STR(n->marked));\n\t\tfprintf(rsp, \"\\\"presel\\\":\");\n\t\tquery_presel(n->presel, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"rectangle\\\":\");\n\t\tquery_rectangle(n->rectangle, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"constraints\\\":\");\n\t\tquery_constraints(n->constraints, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"firstChild\\\":\");\n\t\tquery_node(n->first_child, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"secondChild\\\":\");\n\t\tquery_node(n->second_child, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"client\\\":\");\n\t\tquery_client(n->client, rsp);\n\t\tfprintf(rsp, \"}\");\n\t}\n}\n\nvoid query_presel(presel_t *p, FILE *rsp)\n{\n\tif (p == NULL) {\n\t\tfprintf(rsp, \"null\");\n\t} else {\n\t\tfprintf(rsp, \"{\\\"splitDir\\\":\\\"%s\\\",\\\"splitRatio\\\":%lf}\", SPLIT_DIR_STR(p->split_dir), p->split_ratio);\n\t}\n}\n\nvoid query_client(client_t *c, FILE *rsp)\n{\n\tif (c == NULL) {\n\t\tfprintf(rsp, \"null\");\n\t} else {\n\t\tfprintf(rsp, \"{\");\n\t\tfprintf(rsp, \"\\\"className\\\":\\\"%s\\\",\", c->class_name);\n\t\tfprintf(rsp, \"\\\"instanceName\\\":\\\"%s\\\",\", c->instance_name);\n\t\tfprintf(rsp, \"\\\"borderWidth\\\":%u,\", c->border_width);\n\t\tfprintf(rsp, \"\\\"state\\\":\\\"%s\\\",\", STATE_STR(c->state));\n\t\tfprintf(rsp, \"\\\"lastState\\\":\\\"%s\\\",\", STATE_STR(c->last_state));\n\t\tfprintf(rsp, \"\\\"layer\\\":\\\"%s\\\",\", LAYER_STR(c->layer));\n\t\tfprintf(rsp, \"\\\"lastLayer\\\":\\\"%s\\\",\", LAYER_STR(c->last_layer));\n\t\tfprintf(rsp, \"\\\"urgent\\\":%s,\", BOOL_STR(c->urgent));\n\t\tfprintf(rsp, \"\\\"shown\\\":%s,\", BOOL_STR(c->shown));\n\t\tfprintf(rsp, \"\\\"tiledRectangle\\\":\");\n\t\tquery_rectangle(c->tiled_rectangle, rsp);\n\t\tfprintf(rsp,\",\");\n\t\tfprintf(rsp, \"\\\"floatingRectangle\\\":\");\n\t\tquery_rectangle(c->floating_rectangle, rsp);\n\t\tfprintf(rsp, \"}\");\n\t}\n}\n\nvoid query_rectangle(xcb_rectangle_t r, FILE *rsp)\n{\n\tfprintf(rsp, \"{\\\"x\\\":%i,\\\"y\\\":%i,\\\"width\\\":%u,\\\"height\\\":%u}\", r.x, r.y, r.width, r.height);\n}\n\nvoid query_constraints(constraints_t c, FILE *rsp)\n{\n\tfprintf(rsp, \"{\\\"min_width\\\":%u,\\\"min_height\\\":%u}\", c.min_width, c.min_height);\n}\n\nvoid query_padding(padding_t p, FILE *rsp)\n{\n\tfprintf(rsp, \"{\\\"top\\\":%i,\\\"right\\\":%i,\\\"bottom\\\":%i,\\\"left\\\":%i}\", p.top, p.right, p.bottom, p.left);\n}\n\nvoid query_history(FILE *rsp)\n{\n\tfprintf(rsp, \"[\");\n\tfor (history_t *h = history_head; h != NULL; h = h->next) {\n\t\tquery_coordinates(&h->loc, rsp);\n\t\tif (h->next != NULL) {\n\t\t\tfprintf(rsp, \",\");\n\t\t}\n\t}\n\tfprintf(rsp, \"]\");\n}\n\nvoid query_coordinates(coordinates_t *loc, FILE *rsp)\n{\n\tfprintf(rsp, \"{\\\"monitorId\\\":%u,\\\"desktopId\\\":%u,\\\"nodeId\\\":%u}\", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);\n}\n\nvoid query_stack(FILE *rsp)\n{\n\tfprintf(rsp, \"[\");\n\tfor (stacking_list_t *s = stack_head; s != NULL; s = s->next) {\n\t\tfprintf(rsp, \"%u\", s->node->id);\n\t\tif (s->next != NULL) {\n\t\t\tfprintf(rsp, \",\");\n\t\t}\n\t}\n\tfprintf(rsp, \"]\");\n}\n\nvoid query_subscribers(FILE *rsp)\n{\n\tfprintf(rsp, \"[\");\n\tfor (subscriber_list_t *s = subscribe_head; s != NULL; s = s->next) {\n\t\tfprintf(rsp, \"{\\\"fileDescriptor\\\": %i\", fileno(s->stream));\n\t\tif (s->fifo_path != NULL) {\n\t\t\tfprintf(rsp, \",\\\"fifoPath\\\":\\\"%s\\\"\", s->fifo_path);\n\t\t}\n\t\tfprintf(rsp, \",\\\"field\\\":%i,\\\"count\\\":%i}\", s->field, s->count);\n\t\tif (s->next != NULL) {\n\t\t\tfprintf(rsp, \",\");\n\t\t}\n\t}\n\tfprintf(rsp, \"]\");\n}\n\nint query_node_ids(coordinates_t *mon_ref, coordinates_t *desk_ref, coordinates_t* ref, coordinates_t *trg, monitor_select_t *mon_sel, desktop_select_t *desk_sel, node_select_t *sel, FILE *rsp)\n{\n\tint count = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tcoordinates_t loc = {m, NULL, NULL};\n\t\tif ((trg->monitor != NULL && m != trg->monitor) ||\n\t\t    (mon_sel != NULL && !monitor_matches(&loc, mon_ref, mon_sel))) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tcoordinates_t loc = {m, d, NULL};\n\t\t\tif ((trg->desktop != NULL && d != trg->desktop) ||\n\t\t\t    (desk_sel != NULL && !desktop_matches(&loc, desk_ref, desk_sel))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcount += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);\n\t\t}\n\t}\n\treturn count;\n}\n\nint query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)\n{\n\tint count = 0;\n\tif (n == NULL) {\n\t\treturn 0;\n\t} else {\n\t\tcoordinates_t loc = {m, d, n};\n\t\tif ((trg->node == NULL || n == trg->node) &&\n\t\t    (sel == NULL || node_matches(&loc, ref, sel))) {\n\t\t\tfprintf(rsp, \"0x%08X\\n\", n->id);\n\t\t\tcount++;\n\t\t}\n\t\tcount += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);\n\t\tcount += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);\n\t}\n\treturn count;\n}\n\nint query_desktop_ids(coordinates_t* mon_ref, coordinates_t *ref, coordinates_t *trg, monitor_select_t *mon_sel, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)\n{\n\tint count = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tcoordinates_t loc = {m, NULL, NULL};\n\t\tif ((trg->monitor != NULL && m != trg->monitor) ||\n\t\t    (mon_sel != NULL && !monitor_matches(&loc, mon_ref, mon_sel))) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tcoordinates_t loc = {m, d, NULL};\n\t\t\tif ((trg->desktop != NULL && d != trg->desktop) ||\n\t\t\t    (sel != NULL && !desktop_matches(&loc, ref, sel))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tprinter(d, rsp);\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn count;\n}\n\nint query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)\n{\n\tint count = 0;\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tcoordinates_t loc = {m, NULL, NULL};\n\t\tif ((trg->monitor != NULL && m != trg->monitor) ||\n\t\t    (sel != NULL && !monitor_matches(&loc, ref, sel))) {\n\t\t\tcontinue;\n\t\t}\n\t\tprinter(m, rsp);\n\t\tcount++;\n\t}\n\treturn count;\n}\n\nvoid fprint_monitor_id(monitor_t *m, FILE *rsp)\n{\n\tfprintf(rsp, \"0x%08X\\n\", m->id);\n}\n\nvoid fprint_monitor_name(monitor_t *m, FILE *rsp)\n{\n\tfprintf(rsp, \"%s\\n\", m->name);\n}\n\nvoid fprint_desktop_id(desktop_t *d, FILE *rsp)\n{\n\tfprintf(rsp, \"0x%08X\\n\", d->id);\n}\n\nvoid fprint_desktop_name(desktop_t *d, FILE *rsp)\n{\n\tfprintf(rsp, \"%s\\n\", d->name);\n}\n\nvoid print_ignore_request(state_transition_t st, FILE *rsp)\n{\n\tif (st == 0) {\n\t\tfprintf(rsp, \"none\");\n\t} else {\n\t\tunsigned int cnt = 0;\n\t\tif (st & STATE_TRANSITION_ENTER) {\n\t\t\tfprintf(rsp, \"enter\");\n\t\t\tcnt++;\n\t\t}\n\t\tif (st & STATE_TRANSITION_EXIT) {\n\t\t\tfprintf(rsp, \"%sexit\", cnt > 0 ? \",\" : \"\");\n\t\t}\n\t}\n}\n\nvoid print_modifier_mask(uint16_t m, FILE *rsp)\n{\n\tswitch (m) {\n\t\tcase XCB_MOD_MASK_SHIFT:\n\t\t\tfprintf(rsp, \"shift\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_CONTROL:\n\t\t\tfprintf(rsp, \"control\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_LOCK:\n\t\t\tfprintf(rsp, \"lock\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_1:\n\t\t\tfprintf(rsp, \"mod1\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_2:\n\t\t\tfprintf(rsp, \"mod2\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_3:\n\t\t\tfprintf(rsp, \"mod3\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_4:\n\t\t\tfprintf(rsp, \"mod4\");\n\t\t\tbreak;\n\t\tcase XCB_MOD_MASK_5:\n\t\t\tfprintf(rsp, \"mod5\");\n\t\t\tbreak;\n\t}\n}\n\nvoid print_button_index(int8_t b, FILE *rsp)\n{\n\tswitch (b) {\n\t\tcase XCB_BUTTON_INDEX_ANY:\n\t\t\tfprintf(rsp, \"any\");\n\t\t\tbreak;\n\t\tcase XCB_BUTTON_INDEX_1:\n\t\t\tfprintf(rsp, \"button1\");\n\t\t\tbreak;\n\t\tcase XCB_BUTTON_INDEX_2:\n\t\t\tfprintf(rsp, \"button2\");\n\t\t\tbreak;\n\t\tcase XCB_BUTTON_INDEX_3:\n\t\t\tfprintf(rsp, \"button3\");\n\t\t\tbreak;\n\t\tcase -1:\n\t\t\tfprintf(rsp, \"none\");\n\t\t\tbreak;\n\t}\n}\n\nvoid print_pointer_action(pointer_action_t a, FILE *rsp)\n{\n\tswitch (a) {\n\t\tcase ACTION_MOVE:\n\t\t\tfprintf(rsp, \"move\");\n\t\t\tbreak;\n\t\tcase ACTION_RESIZE_SIDE:\n\t\t\tfprintf(rsp, \"resize_side\");\n\t\t\tbreak;\n\t\tcase ACTION_RESIZE_CORNER:\n\t\t\tfprintf(rsp, \"resize_corner\");\n\t\t\tbreak;\n\t\tcase ACTION_FOCUS:\n\t\t\tfprintf(rsp, \"focus\");\n\t\t\tbreak;\n\t\tcase ACTION_NONE:\n\t\t\tfprintf(rsp, \"none\");\n\t\t\tbreak;\n\t}\n}\n\nvoid resolve_rule_consequence(rule_consequence_t *csq)\n{\n\tcoordinates_t ref = {mon, mon->desk, mon->desk->focus};\n\tcoordinates_t dst = {NULL, NULL, NULL};\n\tmonitor_t *monitor = monitor_from_desc(csq->monitor_desc, &ref, &dst) != SELECTOR_OK ? NULL : dst.monitor;\n\tdesktop_t *desktop = desktop_from_desc(csq->desktop_desc, &ref, &dst) != SELECTOR_OK ? NULL : dst.desktop;\n\tnode_t *node = node_from_desc(csq->node_desc, &ref, &dst) != SELECTOR_OK ? NULL : dst.node;\n\n#define PRINT_OBJECT_ID(name) \\\n\tif (name == NULL) { \\\n\t\tcsq->name##_desc[0] = '\\0'; \\\n\t} else { \\\n\t\tsnprintf(csq->name##_desc, 11, \"0x%08X\", name->id); \\\n\t}\n\tPRINT_OBJECT_ID(monitor)\n\tPRINT_OBJECT_ID(desktop)\n\tPRINT_OBJECT_ID(node)\n#undef PRINT_OBJECT_ID\n}\n\nvoid print_rule_consequence(char **buf, rule_consequence_t *csq)\n{\n\tchar *rect_buf = NULL;\n\tprint_rectangle(&rect_buf, csq->rect);\n\tif (rect_buf == NULL) {\n\t\trect_buf = malloc(1);\n\t\t*rect_buf = '\\0';\n\t}\n\n\tasprintf(buf, \"monitor=%s desktop=%s node=%s state=%s layer=%s honor_size_hints=%s split_dir=%s split_ratio=%lf hidden=%s sticky=%s private=%s locked=%s marked=%s center=%s follow=%s manage=%s focus=%s border=%s rectangle=%s\",\n\t        csq->monitor_desc, csq->desktop_desc, csq->node_desc,\n\t        csq->state == NULL ? \"\" : STATE_STR(*csq->state),\n\t        csq->layer == NULL ? \"\" : LAYER_STR(*csq->layer),\n\t        csq->honor_size_hints == HONOR_SIZE_HINTS_DEFAULT ? \"\" : HSH_MODE_STR(csq->honor_size_hints),\n\t        csq->split_dir == NULL ? \"\" : SPLIT_DIR_STR(*csq->split_dir), csq->split_ratio,\n\t        ON_OFF_STR(csq->hidden), ON_OFF_STR(csq->sticky), ON_OFF_STR(csq->private),\n\t        ON_OFF_STR(csq->locked), ON_OFF_STR(csq->marked), ON_OFF_STR(csq->center), ON_OFF_STR(csq->follow),\n\t        ON_OFF_STR(csq->manage), ON_OFF_STR(csq->focus), ON_OFF_STR(csq->border), rect_buf);\n\tfree(rect_buf);\n}\n\nvoid print_rectangle(char **buf, xcb_rectangle_t *rect)\n{\n\tif (rect != NULL) {\n\t\tasprintf(buf, \"%hux%hu+%hi+%hi\", rect->width, rect->height, rect->x, rect->y);\n\t}\n}\n\nnode_select_t make_node_select(void)\n{\n\tnode_select_t sel = {\n\t\t.automatic = OPTION_NONE,\n\t\t.focused = OPTION_NONE,\n\t\t.active = OPTION_NONE,\n\t\t.local = OPTION_NONE,\n\t\t.leaf = OPTION_NONE,\n\t\t.window = OPTION_NONE,\n\t\t.tiled = OPTION_NONE,\n\t\t.pseudo_tiled = OPTION_NONE,\n\t\t.floating = OPTION_NONE,\n\t\t.fullscreen = OPTION_NONE,\n\t\t.hidden = OPTION_NONE,\n\t\t.sticky = OPTION_NONE,\n\t\t.private = OPTION_NONE,\n\t\t.locked = OPTION_NONE,\n\t\t.marked = OPTION_NONE,\n\t\t.urgent = OPTION_NONE,\n\t\t.same_class = OPTION_NONE,\n\t\t.descendant_of = OPTION_NONE,\n\t\t.ancestor_of = OPTION_NONE,\n\t\t.below = OPTION_NONE,\n\t\t.normal = OPTION_NONE,\n\t\t.above = OPTION_NONE,\n\t\t.horizontal = OPTION_NONE,\n\t\t.vertical = OPTION_NONE\n\t};\n\treturn sel;\n}\n\ndesktop_select_t make_desktop_select(void)\n{\n\tdesktop_select_t sel = {\n\t\t.occupied = OPTION_NONE,\n\t\t.focused = OPTION_NONE,\n\t\t.active = OPTION_NONE,\n\t\t.urgent = OPTION_NONE,\n\t\t.local = OPTION_NONE,\n\t\t.tiled = OPTION_NONE,\n\t\t.monocle = OPTION_NONE,\n\t\t.user_tiled = OPTION_NONE,\n\t\t.user_monocle = OPTION_NONE\n\t};\n\treturn sel;\n}\n\nmonitor_select_t make_monitor_select(void)\n{\n\tmonitor_select_t sel = {\n\t\t.occupied = OPTION_NONE,\n\t\t.focused = OPTION_NONE\n\t};\n\treturn sel;\n}\n\nint node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)\n{\n\tdst->node = NULL;\n\n\tcoordinates_t ref_copy = *ref;\n\tref = &ref_copy;\n\tchar *desc_copy = copy_string(desc, strlen(desc));\n\tdesc = desc_copy;\n\n\tchar *hash = strrchr(desc, '#');\n\tchar *path = strrchr(desc, '@');\n\tchar *colon = strrchr(desc, ':');\n\n\t/* Adjust or discard hashes inside a DESKTOP_SEL, e.g. `newest#@prev#older:/1/2` */\n\tif (hash != NULL && colon != NULL && path != NULL &&\n\t    path < hash && hash < colon) {\n\t\tif (path > desc && *(path - 1) == '#') {\n\t\t\thash = path - 1;\n\t\t} else {\n\t\t\thash = NULL;\n\t\t}\n\t}\n\n\tif (hash != NULL) {\n\t\t*hash = '\\0';\n\t\tint ret;\n\t\tcoordinates_t tmp = {mon, mon->desk, mon->desk->focus};\n\t\tif ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {\n\t\t\tdesc = hash + 1;\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/* Discard colons within references, e.g. `@next.occupied:/#any.descendant_of.window` */\n\tif (colon != NULL && hash != NULL && colon < hash) {\n\t\tcolon = NULL;\n\t}\n\n\tnode_select_t sel = make_node_select();\n\n\tif (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {\n\t\tfree(desc_copy);\n\t\treturn SELECTOR_BAD_MODIFIERS;\n\t}\n\n\tdirection_t dir;\n\tcycle_dir_t cyc;\n\thistory_dir_t hdi;\n\tif (parse_direction(desc, &dir)) {\n\t\tfind_nearest_neighbor(ref, dst, dir, &sel);\n\t} else if (parse_cycle_direction(desc, &cyc)) {\n\t\tfind_closest_node(ref, dst, cyc, &sel);\n\t} else if (parse_history_direction(desc, &hdi)) {\n\t\thistory_find_node(hdi, ref, dst, &sel);\n\t} else if (streq(\"any\", desc)) {\n\t\tfind_any_node(ref, dst, &sel);\n\t} else if (streq(\"first_ancestor\", desc)) {\n\t\tfind_first_ancestor(ref, dst, &sel);\n\t} else if (streq(\"last\", desc)) {\n\t\thistory_find_node(HISTORY_OLDER, ref, dst, &sel);\n\t} else if (streq(\"newest\", desc)) {\n\t\thistory_find_newest_node(ref, dst, &sel);\n\t} else if (streq(\"biggest\", desc)) {\n\t\tfind_by_area(AREA_BIGGEST, ref, dst, &sel);\n\t} else if (streq(\"smallest\", desc)) {\n\t\tfind_by_area(AREA_SMALLEST, ref, dst, &sel);\n\t} else if (streq(\"pointed\", desc)) {\n\t\txcb_window_t win = XCB_NONE;\n\t\tquery_pointer(&win, NULL);\n\t\tif (locate_leaf(win, dst) && node_matches(dst, ref, &sel)) {\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t} else if (streq(\"focused\", desc)) {\n\t\tcoordinates_t loc = {mon, mon->desk, mon->desk->focus};\n\t\tif (node_matches(&loc, ref, &sel)) {\n\t\t\t*dst = loc;\n\t\t}\n\t} else if (*desc == '@') {\n\t\tdesc++;\n\t\t*dst = *ref;\n\t\tif (colon != NULL) {\n\t\t\t*colon = '\\0';\n\t\t\tint ret;\n\t\t\tif ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {\n\t\t\t\tdst->node = dst->desktop->focus;\n\t\t\t\tdesc = colon + 1;\n\t\t\t} else {\n\t\t\t\tfree(desc_copy);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t\tif (*desc == '/') {\n\t\t\tdst->node = dst->desktop->root;\n\t\t}\n\t\tchar *move = strtok(desc, PTH_TOK);\n\t\twhile (move != NULL && dst->node != NULL) {\n\t\t\tif (streq(\"first\", move) || streq(\"1\", move)) {\n\t\t\t\tdst->node = dst->node->first_child;\n\t\t\t} else if (streq(\"second\", move) || streq(\"2\", move)) {\n\t\t\t\tdst->node = dst->node->second_child;\n\t\t\t} else if (streq(\"parent\", move)) {\n\t\t\t\tdst->node = dst->node->parent;\n\t\t\t} else if (streq(\"brother\", move)) {\n\t\t\t\tdst->node = brother_tree(dst->node);\n\t\t\t} else {\n\t\t\t\tdirection_t dir;\n\t\t\t\tif (parse_direction(move, &dir)) {\n\t\t\t\t\tdst->node = find_fence(dst->node, dir);\n\t\t\t\t} else {\n\t\t\t\t\tfree(desc_copy);\n\t\t\t\t\treturn SELECTOR_BAD_DESCRIPTOR;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmove = strtok(NULL, PTH_TOK);\n\t\t}\n\t\tfree(desc_copy);\n\t\tif (dst->node != NULL) {\n\t\t\tif (node_matches(dst, ref, &sel)) {\n\t\t\t\treturn SELECTOR_OK;\n\t\t\t} else {\n\t\t\t\treturn SELECTOR_INVALID;\n\t\t\t}\n\t\t} else if (dst->desktop->root != NULL) {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t\treturn SELECTOR_OK;\n\t} else {\n\t\tuint32_t id;\n\t\tif (parse_id(desc, &id)) {\n\t\t\tfree(desc_copy);\n\t\t\tif (find_by_id(id, dst) && node_matches(dst, ref, &sel)) {\n\t\t\t\treturn SELECTOR_OK;\n\t\t\t} else {\n\t\t\t\treturn SELECTOR_INVALID;\n\t\t\t}\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn SELECTOR_BAD_DESCRIPTOR;\n\t\t}\n\t}\n\n\tfree(desc_copy);\n\n\tif (dst->node == NULL) {\n\t\treturn SELECTOR_INVALID;\n\t}\n\n\treturn SELECTOR_OK;\n}\n\nint desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)\n{\n\tdst->desktop = NULL;\n\n\tif (*desc == '%') {\n\t\tlocate_desktop(desc + 1, dst);\n\t\tgoto end;\n\t}\n\n\tcoordinates_t ref_copy = *ref;\n\tref = &ref_copy;\n\tchar *desc_copy = copy_string(desc, strlen(desc));\n\tdesc = desc_copy;\n\n\tchar *hash = strrchr(desc, '#');\n\tchar *colon = strrchr(desc, ':');\n\n\t/* Discard hashes inside a MONITOR_SEL, e.g. `primary#next:focused` */\n\tif (hash != NULL && colon != NULL && hash < colon) {\n\t\thash = NULL;\n\t}\n\n\tif (hash != NULL) {\n\t\t*hash = '\\0';\n\t\tint ret;\n\t\tcoordinates_t tmp = {mon, mon->desk, NULL};\n\t\tif ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {\n\t\t\tdesc = hash + 1;\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/* Discard colons within references, e.g. `DisplayPort-1:focused#next.local` */\n\tif (colon != NULL && hash != NULL && colon < hash) {\n\t\tcolon = NULL;\n\t}\n\n\tdesktop_select_t sel = make_desktop_select();\n\n\tif (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {\n\t\tfree(desc_copy);\n\t\treturn SELECTOR_BAD_MODIFIERS;\n\t}\n\n\tcycle_dir_t cyc;\n\thistory_dir_t hdi;\n\tuint16_t idx;\n\tuint32_t id;\n\tif (parse_cycle_direction(desc, &cyc)) {\n\t\tfind_closest_desktop(ref, dst, cyc, &sel);\n\t} else if (parse_history_direction(desc, &hdi)) {\n\t\thistory_find_desktop(hdi, ref, dst, &sel);\n\t} else if (streq(\"any\", desc)) {\n\t\tfind_any_desktop(ref, dst, &sel);\n\t} else if (streq(\"last\", desc)) {\n\t\thistory_find_desktop(HISTORY_OLDER, ref, dst, &sel);\n\t} else if (streq(\"newest\", desc)) {\n\t\thistory_find_newest_desktop(ref, dst, &sel);\n\t} else if (streq(\"focused\", desc)) {\n\t\tcoordinates_t loc = {mon, mon->desk, NULL};\n\t\tif (desktop_matches(&loc, ref, &sel)) {\n\t\t\t*dst = loc;\n\t\t}\n\t} else if (colon != NULL) {\n\t\t*colon = '\\0';\n\t\tint ret;\n\t\tif ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {\n\t\t\tif (streq(\"focused\", colon + 1)) {\n\t\t\t\tcoordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};\n\t\t\t\tif (desktop_matches(&loc, ref, &sel)) {\n\t\t\t\t\t*dst = loc;\n\t\t\t\t}\n\t\t\t} else if (parse_index(colon + 1, &idx)) {\n\t\t\t\tfree(desc_copy);\n\t\t\t\tif (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, &sel)) {\n\t\t\t\t\treturn SELECTOR_OK;\n\t\t\t\t} else {\n\t\t\t\t\treturn SELECTOR_INVALID;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfree(desc_copy);\n\t\t\t\treturn SELECTOR_BAD_DESCRIPTOR;\n\t\t\t}\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn ret;\n\t\t}\n\t} else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {\n\t\tfree(desc_copy);\n\t\tif (desktop_matches(dst, ref, &sel)) {\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t} else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {\n\t\tfree(desc_copy);\n\t\tif (desktop_matches(dst, ref, &sel)) {\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t} else {\n\t\tint hits = 0;\n\t\tif (desktop_from_name(desc, ref, dst, &sel, &hits)) {\n\t\t\tfree(desc_copy);\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\tif (hits > 0) {\n\t\t\t\treturn SELECTOR_INVALID;\n\t\t\t} else {\n\t\t\t\treturn SELECTOR_BAD_DESCRIPTOR;\n\t\t\t}\n\t\t}\n\t}\n\n\tfree(desc_copy);\n\nend:\n\tif (dst->desktop == NULL) {\n\t\treturn SELECTOR_INVALID;\n\t}\n\n\treturn SELECTOR_OK;\n}\n\nint monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)\n{\n\tdst->monitor = NULL;\n\n\tif (*desc == '%') {\n\t\tlocate_monitor(desc + 1, dst);\n\t\tgoto end;\n\t}\n\n\tcoordinates_t ref_copy = *ref;\n\tref = &ref_copy;\n\tchar *desc_copy = copy_string(desc, strlen(desc));\n\tdesc = desc_copy;\n\n\tchar *hash = strrchr(desc, '#');\n\n\tif (hash != NULL) {\n\t\t*hash = '\\0';\n\t\tint ret;\n\t\tcoordinates_t tmp = {mon, NULL, NULL};\n\t\tif ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {\n\t\t\tdesc = hash + 1;\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\tmonitor_select_t sel = make_monitor_select();\n\n\tif (!parse_monitor_modifiers(desc, &sel)) {\n\t\tfree(desc_copy);\n\t\treturn SELECTOR_BAD_MODIFIERS;\n\t}\n\n\tdirection_t dir;\n\tcycle_dir_t cyc;\n\thistory_dir_t hdi;\n\tuint16_t idx;\n\tuint32_t id;\n\tif (parse_direction(desc, &dir)) {\n\t\tdst->monitor = nearest_monitor(ref->monitor, dir, &sel);\n\t} else if (parse_cycle_direction(desc, &cyc)) {\n\t\tdst->monitor = closest_monitor(ref->monitor, cyc, &sel);\n\t} else if (parse_history_direction(desc, &hdi)) {\n\t\thistory_find_monitor(hdi, ref, dst, &sel);\n\t} else if (streq(\"any\", desc)) {\n\t\tfind_any_monitor(ref, dst, &sel);\n\t} else if (streq(\"last\", desc)) {\n\t\thistory_find_monitor(HISTORY_OLDER, ref, dst, &sel);\n\t} else if (streq(\"newest\", desc)) {\n\t\thistory_find_newest_monitor(ref, dst, &sel);\n\t} else if (streq(\"primary\", desc)) {\n\t\tif (pri_mon != NULL) {\n\t\t\tcoordinates_t loc = {pri_mon, NULL, NULL};\n\t\t\tif (monitor_matches(&loc, ref, &sel)) {\n\t\t\t\tdst->monitor = pri_mon;\n\t\t\t}\n\t\t}\n\t} else if (streq(\"focused\", desc)) {\n\t\tcoordinates_t loc = {mon, NULL, NULL};\n\t\tif (monitor_matches(&loc, ref, &sel)) {\n\t\t\tdst->monitor = mon;\n\t\t}\n\t} else if (streq(\"pointed\", desc)) {\n\t\txcb_point_t pointer;\n\t\tquery_pointer(NULL, &pointer);\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\tif (is_inside(pointer, m->rectangle)) {\n\t\t\t\tdst->monitor = m;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {\n\t\tfree(desc_copy);\n\t\tif (monitor_matches(dst, ref, &sel)) {\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t} else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {\n\t\tfree(desc_copy);\n\t\tif (monitor_matches(dst, ref, &sel)) {\n\t\t\treturn SELECTOR_OK;\n\t\t} else {\n\t\t\treturn SELECTOR_INVALID;\n\t\t}\n\t} else {\n\t\tif (locate_monitor(desc, dst)) {\n\t\t\tfree(desc_copy);\n\t\t\tif (monitor_matches(dst, ref, &sel)) {\n\t\t\t\treturn SELECTOR_OK;\n\t\t\t} else {\n\t\t\t\treturn SELECTOR_INVALID;\n\t\t\t}\n\t\t} else {\n\t\t\tfree(desc_copy);\n\t\t\treturn SELECTOR_BAD_DESCRIPTOR;\n\t\t}\n\t}\n\n\tfree(desc_copy);\n\nend:\n\tif (dst->monitor == NULL) {\n\t\treturn SELECTOR_INVALID;\n\t}\n\n\treturn SELECTOR_OK;\n}\n\nbool locate_leaf(xcb_window_t win, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\tif (n->id == win) {\n\t\t\t\t\tloc->monitor = m;\n\t\t\t\t\tloc->desktop = d;\n\t\t\t\t\tloc->node = n;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool locate_window(xcb_window_t win, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\tif (n->client == NULL) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (n->id == win) {\n\t\t\t\t\tloc->monitor = m;\n\t\t\t\t\tloc->desktop = d;\n\t\t\t\t\tloc->node = n;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool locate_desktop(char *name, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tif (streq(d->name, name)) {\n\t\t\t\tloc->monitor = m;\n\t\t\t\tloc->desktop = d;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool locate_monitor(char *name, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (streq(m->name, name)) {\n\t\t\tloc->monitor = m;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (mm != NULL && m != mm) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tif (d->id == id) {\n\t\t\t\tloc->monitor = m;\n\t\t\t\tloc->desktop = d;\n\t\t\t\tloc->node = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool desktop_from_name(char *name, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel, int *hits)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tif (streq(d->name, name)) {\n\t\t\t\tif (hits != NULL) {\n\t\t\t\t\t(*hits)++;\n\t\t\t\t}\n\t\t\t\tcoordinates_t loc = {m, d, NULL};\n\t\t\t\tif (desktop_matches(&loc, ref, sel)) {\n\t\t\t\t\tdst->monitor = m;\n\t\t\t\t\tdst->desktop = d;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (mm != NULL && m != mm) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {\n\t\t\tif (idx == 1) {\n\t\t\t\tloc->monitor = m;\n\t\t\t\tloc->desktop = d;\n\t\t\t\tloc->node = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nbool monitor_from_id(uint32_t id, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tif (m->id == id) {\n\t\t\tloc->monitor = m;\n\t\t\tloc->desktop = NULL;\n\t\t\tloc->node = NULL;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool monitor_from_index(int idx, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {\n\t\tif (idx == 1) {\n\t\t\tloc->monitor = m;\n\t\t\tloc->desktop = NULL;\n\t\t\tloc->node = NULL;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t *sel)\n{\n\tif (loc->node == NULL) {\n\t\treturn false;\n\t}\n\n\tif (sel->focused != OPTION_NONE &&\n\t    loc->node != mon->desk->focus\n\t    ? sel->focused == OPTION_TRUE\n\t    : sel->focused == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->active != OPTION_NONE &&\n\t    loc->node != loc->desktop->focus\n\t    ? sel->active == OPTION_TRUE\n\t    : sel->active == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->automatic != OPTION_NONE &&\n\t    loc->node->presel != NULL\n\t    ? sel->automatic == OPTION_TRUE\n\t    : sel->automatic == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->local != OPTION_NONE &&\n\t    loc->desktop != ref->desktop\n\t    ? sel->local == OPTION_TRUE\n\t    : sel->local == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->active != OPTION_NONE &&\n\t    loc->desktop != loc->monitor->desk\n\t    ? sel->active == OPTION_TRUE\n\t    : sel->active == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->leaf != OPTION_NONE &&\n\t    !is_leaf(loc->node)\n\t    ? sel->leaf == OPTION_TRUE\n\t    : sel->leaf == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->window != OPTION_NONE &&\n\t    loc->node->client == NULL\n\t    ? sel->window == OPTION_TRUE\n\t    : sel->window == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n#define NFLAG(p) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    !loc->node->p \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tNFLAG(hidden)\n\tNFLAG(sticky)\n\tNFLAG(private)\n\tNFLAG(locked)\n\tNFLAG(marked)\n#undef NFLAG\n\n#define NSPLIT(p, e) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    loc->node->split_type != e \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tNSPLIT(horizontal, TYPE_HORIZONTAL)\n\tNSPLIT(vertical, TYPE_VERTICAL)\n#undef NSPLIT\n\n\tif (sel->descendant_of != OPTION_NONE &&\n\t    !is_descendant(loc->node, ref->node)\n\t    ? sel->descendant_of == OPTION_TRUE\n\t    : sel->descendant_of == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->ancestor_of != OPTION_NONE &&\n\t    !is_descendant(ref->node, loc->node)\n\t    ? sel->ancestor_of == OPTION_TRUE\n\t    : sel->ancestor_of == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (loc->node->client == NULL) {\n\t\tif (sel->same_class == OPTION_TRUE ||\n\t\t    sel->tiled == OPTION_TRUE ||\n\t\t    sel->pseudo_tiled == OPTION_TRUE ||\n\t\t    sel->floating == OPTION_TRUE ||\n\t\t    sel->fullscreen == OPTION_TRUE ||\n\t\t    sel->below == OPTION_TRUE ||\n\t\t    sel->normal == OPTION_TRUE ||\n\t\t    sel->above == OPTION_TRUE ||\n\t\t    sel->urgent == OPTION_TRUE) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (ref->node != NULL && ref->node->client != NULL &&\n\t    sel->same_class != OPTION_NONE &&\n\t    streq(loc->node->client->class_name, ref->node->client->class_name)\n\t    ? sel->same_class == OPTION_FALSE\n\t    : sel->same_class == OPTION_TRUE) {\n\t\treturn false;\n\t}\n\n#define WSTATE(p, e) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    loc->node->client->state != e \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tWSTATE(tiled, STATE_TILED)\n\tWSTATE(pseudo_tiled, STATE_PSEUDO_TILED)\n\tWSTATE(floating, STATE_FLOATING)\n\tWSTATE(fullscreen, STATE_FULLSCREEN)\n#undef WSTATE\n\n#define WLAYER(p, e) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    loc->node->client->layer != e \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tWLAYER(below, LAYER_BELOW)\n\tWLAYER(normal, LAYER_NORMAL)\n\tWLAYER(above, LAYER_ABOVE)\n#undef WLAYER\n\n#define WFLAG(p) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    !loc->node->client->p \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tWFLAG(urgent)\n#undef WFLAG\n\n\treturn true;\n}\n\nbool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t *sel)\n{\n\tif (sel->occupied != OPTION_NONE &&\n\t    loc->desktop->root == NULL\n\t    ? sel->occupied == OPTION_TRUE\n\t    : sel->occupied == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->focused != OPTION_NONE &&\n\t    loc->desktop != mon->desk\n\t    ? sel->focused == OPTION_TRUE\n\t    : sel->focused == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->active != OPTION_NONE &&\n\t    loc->desktop != loc->monitor->desk\n\t    ? sel->active == OPTION_TRUE\n\t    : sel->active == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->urgent != OPTION_NONE &&\n\t    !is_urgent(loc->desktop)\n\t    ? sel->urgent == OPTION_TRUE\n\t    : sel->urgent == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->local != OPTION_NONE &&\n\t    ref->monitor != loc->monitor\n\t    ? sel->local == OPTION_TRUE\n\t    : sel->local == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n#define DLAYOUT(p, e) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    loc->desktop->layout != e \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tDLAYOUT(tiled, LAYOUT_TILED)\n\tDLAYOUT(monocle, LAYOUT_MONOCLE)\n#undef DLAYOUT\n\n#define DUSERLAYOUT(p, e) \\\n\tif (sel->p != OPTION_NONE && \\\n\t    loc->desktop->user_layout != e \\\n\t    ? sel->p == OPTION_TRUE \\\n\t    : sel->p == OPTION_FALSE) { \\\n\t\treturn false; \\\n\t}\n\tDUSERLAYOUT(user_tiled, LAYOUT_TILED)\n\tDUSERLAYOUT(user_monocle, LAYOUT_MONOCLE)\n#undef DUSERLAYOUT\n\n\treturn true;\n}\n\nbool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t *sel)\n{\n\tif (sel->occupied != OPTION_NONE &&\n\t    loc->monitor->desk->root == NULL\n\t    ? sel->occupied == OPTION_TRUE\n\t    : sel->occupied == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\tif (sel->focused != OPTION_NONE &&\n\t    loc->monitor != mon\n\t    ? sel->focused == OPTION_TRUE\n\t    : sel->focused == OPTION_FALSE) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/query.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_QUERY_H\n#define BSPWM_QUERY_H\n\n#define PTH_TOK  \"/\"\n\ntypedef enum {\n\tDOMAIN_TREE,\n\tDOMAIN_MONITOR,\n\tDOMAIN_DESKTOP,\n\tDOMAIN_NODE\n} domain_t;\n\nenum {\n\tSELECTOR_OK,\n\tSELECTOR_INVALID,\n\tSELECTOR_BAD_MODIFIERS,\n\tSELECTOR_BAD_DESCRIPTOR\n};\n\ntypedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp);\ntypedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp);\n\nvoid query_state(FILE *rsp);\nvoid query_monitor(monitor_t *m, FILE *rsp);\nvoid query_desktop(desktop_t *d, FILE *rsp);\nvoid query_node(node_t *n, FILE *rsp);\nvoid query_presel(presel_t *p, FILE *rsp);\nvoid query_client(client_t *c, FILE *rsp);\nvoid query_rectangle(xcb_rectangle_t r, FILE *rsp);\nvoid query_constraints(constraints_t c, FILE *rsp);\nvoid query_padding(padding_t p, FILE *rsp);\nvoid query_history(FILE *rsp);\nvoid query_coordinates(coordinates_t *loc, FILE *rsp);\nvoid query_stack(FILE *rsp);\nvoid query_subscribers(FILE *rsp);\nint query_node_ids(coordinates_t *mon_ref, coordinates_t *desk_ref, coordinates_t* ref, coordinates_t *trg, monitor_select_t *mon_sel, desktop_select_t *desk_sel, node_select_t *sel, FILE *rsp);\nint query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);\nint query_desktop_ids(coordinates_t* mon_ref, coordinates_t *ref, coordinates_t *trg, monitor_select_t *mon_sel, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);\nint query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);\nvoid fprint_monitor_id(monitor_t *m, FILE *rsp);\nvoid fprint_monitor_name(monitor_t *m, FILE *rsp);\nvoid fprint_desktop_id(desktop_t *d, FILE *rsp);\nvoid fprint_desktop_name(desktop_t *d, FILE *rsp);\nvoid print_ignore_request(state_transition_t st, FILE *rsp);\nvoid print_modifier_mask(uint16_t m, FILE *rsp);\nvoid print_button_index(int8_t b, FILE *rsp);\nvoid print_pointer_action(pointer_action_t a, FILE *rsp);\nvoid resolve_rule_consequence(rule_consequence_t *csq);\nvoid print_rule_consequence(char **buf, rule_consequence_t *csq);\nvoid print_rectangle(char **buf, xcb_rectangle_t *rect);\nnode_select_t make_node_select(void);\ndesktop_select_t make_desktop_select(void);\nmonitor_select_t make_monitor_select(void);\nint node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);\nint desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);\nint monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);\nbool locate_leaf(xcb_window_t win, coordinates_t *loc);\nbool locate_window(xcb_window_t win, coordinates_t *loc);\nbool locate_desktop(char *name, coordinates_t *loc);\nbool locate_monitor(char *name, coordinates_t *loc);\nbool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);\nbool desktop_from_name(char *name, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel, int *hits);\nbool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);\nbool monitor_from_id(uint32_t id, coordinates_t *loc);\nbool monitor_from_index(int idx, coordinates_t *loc);\nbool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t *sel);\nbool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t *sel);\nbool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t *sel);\n\n#endif\n"
  },
  {
    "path": "src/restore.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"ewmh.h\"\n#include \"history.h\"\n#include \"pointer.h\"\n#include \"monitor.h\"\n#include \"query.h\"\n#include \"stack.h\"\n#include \"tree.h\"\n#include \"settings.h\"\n#include \"subscribe.h\"\n#include \"restore.h\"\n#include \"window.h\"\n#include \"parse.h\"\n\nbool restore_state(const char *file_path)\n{\n\tsize_t jslen;\n\tchar *json = read_string(file_path, &jslen);\n\n\tif (json == NULL) {\n\t\treturn false;\n\t}\n\n\tint nbtok = 256;\n\tjsmn_parser parser;\n\tjsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));\n\n\tif (tokens == NULL) {\n\t\tperror(\"Restore tree: malloc\");\n\t\tfree(json);\n\t\treturn false;\n\t}\n\n\tjsmn_init(&parser);\n\tint ret;\n\n\twhile ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {\n\t\tnbtok *= 2;\n\t\tjsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));\n\t\tif (rtokens == NULL) {\n\t\t\tperror(\"Restore tree: realloc\");\n\t\t\tfree(tokens);\n\t\t\tfree(json);\n\t\t\treturn false;\n\t\t} else {\n\t\t\ttokens = rtokens;\n\t\t}\n\t}\n\n\tif (ret < 0) {\n\t\twarn(\"Restore tree: jsmn_parse: \");\n\t\tswitch (ret) {\n\t\t\tcase JSMN_ERROR_NOMEM:\n\t\t\t\twarn(\"not enough memory.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase JSMN_ERROR_INVAL:\n\t\t\t\twarn(\"found invalid character inside JSON string.\\n\");\n\t\t\t\tbreak;\n\t\t\tcase JSMN_ERROR_PART:\n\t\t\t\twarn(\"not a full JSON packet.\\n\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\twarn(\"unknown error.\\n\");\n\t\t\t\tbreak;\n\t\t}\n\n\t\tfree(tokens);\n\t\tfree(json);\n\n\t\treturn false;\n\t}\n\n\tint num = tokens[0].size;\n\n\tif (num < 1) {\n\t\tfree(tokens);\n\t\tfree(json);\n\n\t\treturn false;\n\t}\n\n\tmon = NULL;\n\twhile (mon_head != NULL) {\n\t\tremove_monitor(mon_head);\n\t}\n\n\tjsmntok_t *t = tokens + 1;\n\tuint32_t focused_monitor_id = 0, primary_monitor_id = 0;\n\tjsmntok_t *focus_history_token = NULL, *stacking_list_token = NULL;\n\n\tfor (int i = 0; i < num; i++) {\n\t\tif (keyeq(\"focusedMonitorId\", t, json)) {\n\t\t\tt++;\n\t\t\tsscanf(json + t->start, \"%u\", &focused_monitor_id);\n\t\t} else if (keyeq(\"primaryMonitorId\", t, json)) {\n\t\t\tt++;\n\t\t\tsscanf(json + t->start, \"%u\", &primary_monitor_id);\n\t\t} else if (keyeq(\"clientsCount\", t, json)) {\n\t\t\tt++;\n\t\t\tsscanf(json + t->start, \"%u\", &clients_count);\n\t\t} else if (keyeq(\"monitors\", t, json)) {\n\t\t\tt++;\n\t\t\tint s = t->size;\n\t\t\tt++;\n\t\t\tfor (int j = 0; j < s; j++) {\n\t\t\t\tmonitor_t *m = restore_monitor(&t, json);\n\t\t\t\tif (m->desk == NULL) {\n\t\t\t\t\tadd_desktop(m, make_desktop(NULL, XCB_NONE));\n\t\t\t\t}\n\t\t\t\tadd_monitor(m);\n\t\t\t}\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"focusHistory\", t, json)) {\n\t\t\tt++;\n\t\t\tif (mon == NULL) {\n\t\t\t\tfocus_history_token = t;\n\t\t\t}\n\t\t\trestore_history(&t, json);\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"stackingList\", t, json)) {\n\t\t\tt++;\n\t\t\tif (mon == NULL) {\n\t\t\t\tstacking_list_token = t;\n\t\t\t}\n\t\t\trestore_stack(&t, json);\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"eventSubscribers\", t, json)) {\n\t\t\tt++;\n\t\t\trestore_subscribers(&t, json);\n\t\t\tcontinue;\n\t\t}\n\t\tt++;\n\t}\n\n\tif (focused_monitor_id != 0) {\n\t\tcoordinates_t loc;\n\t\tif (monitor_from_id(focused_monitor_id, &loc)) {\n\t\t\tmon = loc.monitor;\n\t\t}\n\t}\n\n\tif (primary_monitor_id != 0) {\n\t\tcoordinates_t loc;\n\t\tif (monitor_from_id(primary_monitor_id, &loc)) {\n\t\t\tpri_mon = loc.monitor;\n\t\t}\n\t}\n\n\tif (focus_history_token != NULL) {\n\t\trestore_history(&focus_history_token, json);\n\t}\n\n\tif (stacking_list_token != NULL) {\n\t\trestore_stack(&stacking_list_token, json);\n\t}\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tm->id = xcb_generate_id(dpy);\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\td->id = xcb_generate_id(dpy);\n\t\t\tregenerate_ids_in(d->root);\n\t\t\trefresh_presel_feedbacks(m, d, d->root);\n\t\t\trestack_presel_feedbacks(d);\n\n\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\tif (n->client == NULL) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tinitialize_client(n);\n\t\t\t\tuint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};\n\t\t\t\txcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);\n\t\t\t\twindow_grab_buttons(n->id);\n\t\t\t}\n\t\t}\n\t}\n\n\tewmh_update_number_of_desktops();\n\tewmh_update_desktop_names();\n\tewmh_update_desktop_viewport();\n\tewmh_update_current_desktop();\n\tewmh_update_client_list(false);\n\tewmh_update_client_list(true);\n\tewmh_update_active_window();\n\n\tfree(tokens);\n\tfree(json);\n\n\treturn true;\n}\n\n#define RESTORE_INT(k, p) \\\n\t} else if (keyeq(#k, *t, json)) { \\\n\t\t(*t)++; \\\n\t\tsscanf(json + (*t)->start, \"%i\", p);\n\n#define RESTORE_UINT(k, p) \\\n\t} else if (keyeq(#k, *t, json)) { \\\n\t\t(*t)++; \\\n\t\tsscanf(json + (*t)->start, \"%u\", p);\n\n#define RESTORE_USINT(k, p) \\\n\t} else if (keyeq(#k, *t, json)) { \\\n\t\t(*t)++; \\\n\t\tsscanf(json + (*t)->start, \"%hu\", p);\n\n#define RESTORE_DOUBLE(k, p) \\\n\t} else if (keyeq(#k, *t, json)) { \\\n\t\t(*t)++; \\\n\t\tsscanf(json + (*t)->start, \"%lf\", p);\n\n#define RESTORE_ANY(k, p, f) \\\n\t} else if (keyeq(#k, *t, json)) { \\\n\t\t(*t)++; \\\n\t\tchar *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \\\n\t\tf(val, p); \\\n\t\tfree(val);\n\n#define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)\n\nmonitor_t *restore_monitor(jsmntok_t **t, char *json)\n{\n\tint num = (*t)->size;\n\t(*t)++;\n\tmonitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);\n\tuint32_t focused_desktop_id = 0;\n\n\tfor (int i = 0; i < num; i++) {\n\t\tif (keyeq(\"name\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsnprintf(m->name, (*t)->end - (*t)->start + 1, \"%s\", json + (*t)->start);\n\t\tRESTORE_UINT(id, &m->id)\n\t\tRESTORE_UINT(randrId, &m->randr_id)\n\t\tRESTORE_BOOL(wired, &m->wired)\n\t\tRESTORE_UINT(stickyCount, &m->sticky_count)\n\t\tRESTORE_INT(windowGap, &m->window_gap)\n\t\tRESTORE_UINT(borderWidth, &m->border_width)\n\t\tRESTORE_UINT(focusedDesktopId, &focused_desktop_id)\n\t\t} else if (keyeq(\"padding\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\trestore_padding(&m->padding, t, json);\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"rectangle\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\trestore_rectangle(&m->rectangle, t, json);\n\t\t\tupdate_root(m, &m->rectangle);\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"desktops\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tint s = (*t)->size;\n\t\t\t(*t)++;\n\t\t\tfor (int j = 0; j < s; j++) {\n\t\t\t\tdesktop_t *d = restore_desktop(t, json);\n\t\t\t\tadd_desktop(m, d);\n\t\t\t}\n\t\t\tcontinue;\n\t\t} else {\n\t\t\twarn(\"Restore monitor: unknown key: '%.*s'.\\n\", (*t)->end - (*t)->start, json + (*t)->start);\n\t\t\t(*t)++;\n\t\t}\n\t\t(*t)++;\n\t}\n\n\tif (focused_desktop_id != 0) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tif (d->id == focused_desktop_id) {\n\t\t\t\tm->desk = d;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn m;\n}\n\ndesktop_t *restore_desktop(jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\tdesktop_t *d = make_desktop(NULL, UINT32_MAX);\n\txcb_window_t focusedNodeId = XCB_NONE;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tif (keyeq(\"name\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsnprintf(d->name, (*t)->end - (*t)->start + 1, \"%s\", json + (*t)->start);\n\t\tRESTORE_UINT(id, &d->id)\n\t\tRESTORE_ANY(layout, &d->layout, parse_layout)\n\t\tRESTORE_ANY(userLayout, &d->user_layout, parse_layout)\n\t\tRESTORE_INT(windowGap, &d->window_gap)\n\t\tRESTORE_UINT(borderWidth, &d->border_width)\n\t\t} else if (keyeq(\"focusedNodeId\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%u\", &focusedNodeId);\n\t\t} else if (keyeq(\"padding\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\trestore_padding(&d->padding, t, json);\n\t\t\tcontinue;\n\t\t} else if (keyeq(\"root\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\td->root = restore_node(t, json);\n\t\t\tcontinue;\n\t\t} else {\n\t\t\twarn(\"Restore desktop: unknown key: '%.*s'.\\n\", (*t)->end - (*t)->start, json + (*t)->start);\n\t\t\t(*t)++;\n\t\t}\n\t\t(*t)++;\n\t}\n\n\tif (focusedNodeId != XCB_NONE) {\n\t\td->focus = find_by_id_in(d->root, focusedNodeId);\n\t}\n\n\treturn d;\n}\n\nnode_t *restore_node(jsmntok_t **t, char *json)\n{\n\tif ((*t)->type == JSMN_PRIMITIVE) {\n\t\t(*t)++;\n\t\treturn NULL;\n\t} else {\n\t\tint s = (*t)->size;\n\t\t(*t)++;\n\t\t/* hack to prevent a new ID from being generated */\n\t\tnode_t *n = make_node(UINT32_MAX);\n\n\t\tfor (int i = 0; i < s; i++) {\n\t\t\tif (keyeq(\"id\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tsscanf(json + (*t)->start, \"%u\", &n->id);\n\t\t\tRESTORE_ANY(splitType, &n->split_type, parse_split_type)\n\t\t\tRESTORE_DOUBLE(splitRatio, &n->split_ratio)\n\t\t\tRESTORE_BOOL(vacant, &n->vacant)\n\t\t\tRESTORE_BOOL(hidden, &n->hidden)\n\t\t\tRESTORE_BOOL(sticky, &n->sticky)\n\t\t\tRESTORE_BOOL(private, &n->private)\n\t\t\tRESTORE_BOOL(locked, &n->locked)\n\t\t\tRESTORE_BOOL(marked, &n->marked)\n\t\t\t} else if (keyeq(\"presel\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tn->presel = restore_presel(t, json);\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"rectangle\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\trestore_rectangle(&n->rectangle, t, json);\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"constraints\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\trestore_constraints(&n->constraints, t, json);\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"firstChild\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tnode_t *fc = restore_node(t, json);\n\t\t\t\tn->first_child = fc;\n\t\t\t\tif (fc != NULL) {\n\t\t\t\t\tfc->parent = n;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"secondChild\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tnode_t *sc = restore_node(t, json);\n\t\t\t\tn->second_child = sc;\n\t\t\t\tif (sc != NULL) {\n\t\t\t\t\tsc->parent = n;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"client\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tn->client = restore_client(t, json);\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\twarn(\"Restore node: unknown key: '%.*s'.\\n\", (*t)->end - (*t)->start, json + (*t)->start);\n\t\t\t\t(*t)++;\n\t\t\t}\n\t\t\t(*t)++;\n\t\t}\n\n\t\treturn n;\n\t}\n}\n\npresel_t *restore_presel(jsmntok_t **t, char *json)\n{\n\tif ((*t)->type == JSMN_PRIMITIVE) {\n\t\t(*t)++;\n\t\treturn NULL;\n\t} else {\n\t\tint s = (*t)->size;\n\t\t(*t)++;\n\t\tpresel_t *p = make_presel();\n\n\t\tfor (int i = 0; i < s; i++) {\n\t\t\tif (keyeq(\"splitRatio\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tsscanf(json + (*t)->start, \"%lf\", &p->split_ratio);\n\t\t\tRESTORE_ANY(splitDir, &p->split_dir, parse_direction)\n\t\t\t}\n\n\t\t\t(*t)++;\n\t\t}\n\n\t\treturn p;\n\t}\n}\n\n\nclient_t *restore_client(jsmntok_t **t, char *json)\n{\n\tif ((*t)->type == JSMN_PRIMITIVE) {\n\t\t(*t)++;\n\t\treturn NULL;\n\t} else {\n\t\tint s = (*t)->size;\n\t\t(*t)++;\n\t\tclient_t *c = make_client();\n\n\t\tfor (int i = 0; i < s; i++) {\n\t\t\tif (keyeq(\"className\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tsnprintf(c->class_name, (*t)->end - (*t)->start + 1, \"%s\", json + (*t)->start);\n\t\t\t} else if (keyeq(\"instanceName\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\tsnprintf(c->instance_name, (*t)->end - (*t)->start + 1, \"%s\", json + (*t)->start);\n\t\t\tRESTORE_ANY(state, &c->state, parse_client_state)\n\t\t\tRESTORE_ANY(lastState, &c->last_state, parse_client_state)\n\t\t\tRESTORE_ANY(layer, &c->layer, parse_stack_layer)\n\t\t\tRESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)\n\t\t\tRESTORE_UINT(borderWidth, &c->border_width)\n\t\t\tRESTORE_BOOL(urgent, &c->urgent)\n\t\t\tRESTORE_BOOL(shown, &c->shown)\n\t\t\t} else if (keyeq(\"tiledRectangle\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\trestore_rectangle(&c->tiled_rectangle, t, json);\n\t\t\t\tcontinue;\n\t\t\t} else if (keyeq(\"floatingRectangle\", *t, json)) {\n\t\t\t\t(*t)++;\n\t\t\t\trestore_rectangle(&c->floating_rectangle, t, json);\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\twarn(\"Restore client: unknown key: '%.*s'.\\n\", (*t)->end - (*t)->start, json + (*t)->start);\n\t\t\t\t(*t)++;\n\t\t\t}\n\n\t\t\t(*t)++;\n\t\t}\n\n\t\treturn c;\n\t}\n}\n\nvoid restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tif (keyeq(\"x\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hi\", &r->x);\n\t\t} else if (keyeq(\"y\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hi\", &r->y);\n\t\t} else if (keyeq(\"width\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hu\", &r->width);\n\t\t} else if (keyeq(\"height\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hu\", &r->height);\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\nvoid restore_constraints(constraints_t *c, jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tif (keyeq(\"min_width\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hu\", &c->min_width);\n\t\t} else if (keyeq(\"min_height\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%hu\", &c->min_height);\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\nvoid restore_padding(padding_t *p, jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tif (keyeq(\"top\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%i\", &p->top);\n\t\t} else if (keyeq(\"right\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%i\", &p->right);\n\t\t} else if (keyeq(\"bottom\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%i\", &p->bottom);\n\t\t} else if (keyeq(\"left\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%i\", &p->left);\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\nvoid restore_history(jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tcoordinates_t loc = {NULL, NULL, NULL};\n\t\trestore_coordinates(&loc, t, json);\n\t\tif (loc.monitor != NULL && loc.desktop != NULL) {\n\t\t\thistory_add(loc.monitor, loc.desktop, loc.node, true);\n\t\t}\n\t}\n}\n\nvoid restore_subscribers(jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tsubscriber_list_t *s = make_subscriber(NULL, NULL, 0, 0);\n\t\trestore_subscriber(s, t, json);\n\t\tadd_subscriber(s);\n\t}\n}\n\nvoid restore_subscriber(subscriber_list_t *s, jsmntok_t **t, char *json)\n{\n\tint n = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < n; i++) {\n\t\tif (keyeq(\"fileDescriptor\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tint fd;\n\t\t\tsscanf(json + (*t)->start, \"%i\", &fd);\n\t\t\ts->stream = fdopen(fd, \"w\");\n\t\t} else if (keyeq(\"fifoPath\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tfree(s->fifo_path);\n\t\t\ts->fifo_path = copy_string(json + (*t)->start, (*t)->end - (*t)->start);\n\t\tRESTORE_INT(field, &s->field)\n\t\tRESTORE_INT(count, &s->count)\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\nvoid restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\tuint32_t id = 0;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tif (keyeq(\"monitorId\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%u\", &id);\n\t\t\tloc->monitor = find_monitor(id);\n\t\t} else if (keyeq(\"desktopId\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%u\", &id);\n\t\t\tloc->desktop = find_desktop_in(id, loc->monitor);\n\t\t} else if (keyeq(\"nodeId\", *t, json)) {\n\t\t\t(*t)++;\n\t\t\tsscanf(json + (*t)->start, \"%u\", &id);\n\t\t\tloc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\nvoid restore_stack(jsmntok_t **t, char *json)\n{\n\tint s = (*t)->size;\n\t(*t)++;\n\n\tfor (int i = 0; i < s; i++) {\n\t\tuint32_t id;\n\t\tsscanf(json + (*t)->start, \"%u\", &id);\n\t\tcoordinates_t loc;\n\t\tif (locate_window(id, &loc)) {\n\t\t\tstack_insert_after(stack_tail, loc.node);\n\t\t}\n\t\t(*t)++;\n\t}\n}\n\n#undef RESTORE_INT\n#undef RESTORE_UINT\n#undef RESTORE_USINT\n#undef RESTORE_DOUBLE\n#undef RESTORE_ANY\n#undef RESTORE_BOOL\n\nbool keyeq(char *s, jsmntok_t *key, char *json)\n{\n\tsize_t n = key->end - key->start;\n\treturn (strlen(s) == n && strncmp(s, json + key->start, n) == 0);\n}\n"
  },
  {
    "path": "src/restore.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_RESTORE_H\n#define BSPWM_RESTORE_H\n\n#include \"jsmn.h\"\n\nbool restore_state(const char *file_path);\nmonitor_t *restore_monitor(jsmntok_t **t, char *json);\ndesktop_t *restore_desktop(jsmntok_t **t, char *json);\nnode_t *restore_node(jsmntok_t **t, char *json);\npresel_t *restore_presel(jsmntok_t **t, char *json);\nclient_t *restore_client(jsmntok_t **t, char *json);\nvoid restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);\nvoid restore_constraints(constraints_t *c, jsmntok_t **t, char *json);\nvoid restore_padding(padding_t *p, jsmntok_t **t, char *json);\nvoid restore_history(jsmntok_t **t, char *json);\nvoid restore_subscribers(jsmntok_t **t, char *json);\nvoid restore_subscriber(subscriber_list_t *s, jsmntok_t **t, char *json);\nvoid restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);\nvoid restore_stack(jsmntok_t **t, char *json);\nbool keyeq(char *s, jsmntok_t *key, char *json);\n\n#endif\n"
  },
  {
    "path": "src/rule.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <sys/types.h>\n#include <string.h>\n#include <unistd.h>\n#include \"bspwm.h\"\n#include \"ewmh.h\"\n#include \"window.h\"\n#include \"query.h\"\n#include \"parse.h\"\n#include \"settings.h\"\n#include \"rule.h\"\n\nrule_t *make_rule(void)\n{\n\trule_t *r = calloc(1, sizeof(rule_t));\n\tr->class_name[0] = r->instance_name[0] = r->name[0] = r->effect[0] = '\\0';\n\tr->next = r->prev = NULL;\n\tr->one_shot = false;\n\treturn r;\n}\n\nvoid add_rule(rule_t *r)\n {\n\tif (rule_head == NULL) {\n\t\trule_head = rule_tail = r;\n\t} else {\n\t\trule_tail->next = r;\n\t\tr->prev = rule_tail;\n\t\trule_tail = r;\n\t}\n}\n\nvoid remove_rule(rule_t *r)\n{\n\tif (r == NULL) {\n\t\treturn;\n\t}\n\trule_t *prev = r->prev;\n\trule_t *next = r->next;\n\tif (prev != NULL) {\n\t\tprev->next = next;\n\t}\n\tif (next != NULL) {\n\t\tnext->prev = prev;\n\t}\n\tif (r == rule_head) {\n\t\trule_head = next;\n\t}\n\tif (r == rule_tail) {\n\t\trule_tail = prev;\n\t}\n\tfree(r);\n}\n\nvoid remove_rule_by_cause(char *cause)\n{\n\trule_t *r = rule_head;\n\tstruct tokenize_state state;\n\tchar *class_name = tokenize_with_escape(&state, cause, COL_TOK[0]);\n\tchar *instance_name = tokenize_with_escape(&state, NULL, COL_TOK[0]);\n\tchar *name = tokenize_with_escape(&state, NULL, COL_TOK[0]);\n\tif (!class_name || !instance_name || !name) {\n\t\tfree(class_name);\n\t\tfree(instance_name);\n\t\tfree(name);\n\t\treturn;\n\t}\n\n\twhile (r != NULL) {\n\t\trule_t *next = r->next;\n\t\tif ((class_name != NULL && (streq(class_name, MATCH_ANY) || streq(r->class_name, class_name))) &&\n\t\t    (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name)) &&\n\t\t    (name == NULL || streq(name, MATCH_ANY) || streq(r->name, name))) {\n\t\t\tremove_rule(r);\n\t\t}\n\t\tr = next;\n\t}\n\tfree(class_name);\n\tfree(instance_name);\n\tfree(name);\n}\n\nbool remove_rule_by_index(int idx)\n{\n\tfor (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {\n\t\tif (idx == 0) {\n\t\t\tremove_rule(r);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nrule_consequence_t *make_rule_consequence(void)\n{\n\trule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));\n\trc->manage = rc->focus = rc->border = true;\n\trc->layer = NULL;\n\trc->state = NULL;\n\trc->rect = NULL;\n\trc->honor_size_hints = HONOR_SIZE_HINTS_DEFAULT;\n\treturn rc;\n}\n\npending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)\n{\n\tpending_rule_t *pr = calloc(1, sizeof(pending_rule_t));\n\tpr->prev = pr->next = NULL;\n\tpr->event_head = pr->event_tail = NULL;\n\tpr->fd = fd;\n\tpr->win = win;\n\tpr->csq = csq;\n\treturn pr;\n}\n\nvoid add_pending_rule(pending_rule_t *pr)\n{\n\tif (pr == NULL) {\n\t\treturn;\n\t}\n\tif (pending_rule_head == NULL) {\n\t\tpending_rule_head = pending_rule_tail = pr;\n\t} else {\n\t\tpending_rule_tail->next = pr;\n\t\tpr->prev = pending_rule_tail;\n\t\tpending_rule_tail = pr;\n\t}\n}\n\nvoid remove_pending_rule(pending_rule_t *pr)\n{\n\tif (pr == NULL) {\n\t\treturn;\n\t}\n\tpending_rule_t *a = pr->prev;\n\tpending_rule_t *b = pr->next;\n\tif (a != NULL) {\n\t\ta->next = b;\n\t}\n\tif (b != NULL) {\n\t\tb->prev = a;\n\t}\n\tif (pr == pending_rule_head) {\n\t\tpending_rule_head = b;\n\t}\n\tif (pr == pending_rule_tail) {\n\t\tpending_rule_tail = a;\n\t}\n\tclose(pr->fd);\n\tfree(pr->csq);\n\tevent_queue_t *eq = pr->event_head;\n\twhile (eq != NULL) {\n\t\tevent_queue_t *next = eq->next;\n\t\tfree(eq);\n\t\teq = next;\n\t}\n\tfree(pr);\n}\n\nvoid postpone_event(pending_rule_t *pr, xcb_generic_event_t *evt)\n{\n\tevent_queue_t *eq = make_event_queue(evt);\n\tif (pr->event_tail == NULL) {\n\t\tpr->event_head = pr->event_tail = eq;\n\t} else {\n\t\tpr->event_tail->next = eq;\n\t\teq->prev = pr->event_tail;\n\t\tpr->event_tail = eq;\n\t}\n}\n\nevent_queue_t *make_event_queue(xcb_generic_event_t *evt)\n{\n\tevent_queue_t *eq = calloc(1, sizeof(event_queue_t));\n\teq->prev = eq->next = NULL;\n\teq->event = *evt;\n\treturn eq;\n}\n\n\n#define SET_CSQ_SPLIT_DIR(val) \\\n\tdo { \\\n\t\tif (csq->split_dir == NULL) { \\\n\t\t\tcsq->split_dir = calloc(1, sizeof(direction_t)); \\\n\t\t} \\\n\t\t*(csq->split_dir) = (val); \\\n\t} while (0)\n\n#define SET_CSQ_STATE(val) \\\n\tdo { \\\n\t\tif (csq->state == NULL) { \\\n\t\t\tcsq->state = calloc(1, sizeof(client_state_t)); \\\n\t\t} \\\n\t\t*(csq->state) = (val); \\\n\t} while (0)\n\n#define SET_CSQ_LAYER(val) \\\n\tdo { \\\n\t\tif (csq->layer == NULL) { \\\n\t\t\tcsq->layer = calloc(1, sizeof(stack_layer_t)); \\\n\t\t} \\\n\t\t*(csq->layer) = (val); \\\n\t} while (0)\n\nvoid _apply_window_type(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_ewmh_get_atoms_reply_t win_type;\n\tif (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {\n\t\tfor (unsigned int i = 0; i < win_type.atoms_len; i++) {\n\t\t\txcb_atom_t a = win_type.atoms[i];\n\t\t\tif (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||\n\t\t\t    a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {\n\t\t\t\tcsq->focus = false;\n\t\t\t} else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {\n\t\t\t\tSET_CSQ_STATE(STATE_FLOATING);\n\t\t\t\tcsq->center = true;\n\t\t\t} else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||\n\t\t\t           a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||\n\t\t\t           a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {\n\t\t\t\tcsq->manage = false;\n\t\t\t\tif (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {\n\t\t\t\t\twindow_lower(win);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\txcb_ewmh_get_atoms_reply_wipe(&win_type);\n\t}\n}\n\nvoid _apply_window_state(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_ewmh_get_atoms_reply_t win_state;\n\tif (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {\n\t\tfor (unsigned int i = 0; i < win_state.atoms_len; i++) {\n\t\t\txcb_atom_t a = win_state.atoms[i];\n\t\t\tif (a == ewmh->_NET_WM_STATE_FULLSCREEN) {\n\t\t\t\tSET_CSQ_STATE(STATE_FULLSCREEN);\n\t\t\t} else if (a == ewmh->_NET_WM_STATE_BELOW) {\n\t\t\t\tSET_CSQ_LAYER(LAYER_BELOW);\n\t\t\t} else if (a == ewmh->_NET_WM_STATE_ABOVE) {\n\t\t\t\tSET_CSQ_LAYER(LAYER_ABOVE);\n\t\t\t} else if (a == ewmh->_NET_WM_STATE_STICKY) {\n\t\t\t\tcsq->sticky = true;\n\t\t\t}\n\t\t}\n\t\txcb_ewmh_get_atoms_reply_wipe(&win_state);\n\t}\n}\n\nvoid _apply_transient(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_window_t transient_for = XCB_NONE;\n\txcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);\n\tif (transient_for != XCB_NONE) {\n\t\tSET_CSQ_STATE(STATE_FLOATING);\n\t}\n}\n\nvoid _apply_hints(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_size_hints_t size_hints;\n\tif (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {\n\t\tif ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&\n\t\t    size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {\n\t\t\tSET_CSQ_STATE(STATE_FLOATING);\n\t\t}\n\t}\n}\n\nvoid _apply_class(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_icccm_get_wm_class_reply_t reply;\n\tif (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {\n\t\tsnprintf(csq->class_name, sizeof(csq->class_name), \"%s\", reply.class_name);\n\t\tsnprintf(csq->instance_name, sizeof(csq->instance_name), \"%s\", reply.instance_name);\n\t\txcb_icccm_get_wm_class_reply_wipe(&reply);\n\t}\n}\n\nvoid _apply_name(xcb_window_t win, rule_consequence_t *csq)\n{\n\txcb_icccm_get_text_property_reply_t reply;\n\tif (xcb_icccm_get_wm_name_reply(dpy, xcb_icccm_get_wm_name(dpy, win), &reply, NULL) == 1) {\n\t\tsnprintf(csq->name, sizeof(csq->name), \"%.*s\", reply.name_len, reply.name);\n\t\txcb_icccm_get_text_property_reply_wipe(&reply);\n\t}\n}\n\nvoid parse_keys_values(char *buf, rule_consequence_t *csq)\n{\n\tchar *key = strtok(buf, CSQ_BLK);\n\tchar *value = strtok(NULL, CSQ_BLK);\n\twhile (key != NULL && value != NULL) {\n\t\tparse_key_value(key, value, csq);\n\t\tkey = strtok(NULL, CSQ_BLK);\n\t\tvalue = strtok(NULL, CSQ_BLK);\n\t}\n}\n\nvoid apply_rules(xcb_window_t win, rule_consequence_t *csq)\n{\n\t_apply_window_type(win, csq);\n\t_apply_window_state(win, csq);\n\t_apply_transient(win, csq);\n\t_apply_hints(win, csq);\n\t_apply_class(win, csq);\n\t_apply_name(win, csq);\n\n\trule_t *rule = rule_head;\n\twhile (rule != NULL) {\n\t\trule_t *next = rule->next;\n\t\tif ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&\n\t\t    (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name)) &&\n\t\t    (streq(rule->name, MATCH_ANY) || streq(rule->name, csq->name))) {\n\t\t\tchar effect[MAXLEN];\n\t\t\tsnprintf(effect, sizeof(effect), \"%s\", rule->effect);\n\t\t\tparse_keys_values(effect, csq);\n\t\t\tif (rule->one_shot) {\n\t\t\t\tremove_rule(rule);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\trule = next;\n\t}\n}\n\nbool schedule_rules(xcb_window_t win, rule_consequence_t *csq)\n{\n\tif (external_rules_command[0] == '\\0') {\n\t\treturn false;\n\t}\n\tresolve_rule_consequence(csq);\n\tint fds[2];\n\tif (pipe(fds) == -1) {\n\t\treturn false;\n\t}\n\tpid_t pid = fork();\n\tif (pid == 0) {\n\t\tif (dpy != NULL) {\n\t\t\tclose(xcb_get_file_descriptor(dpy));\n\t\t}\n\t\tdup2(fds[1], 1);\n\t\tclose(fds[0]);\n\t\tchar wid[SMALEN];\n\t\tchar *csq_buf;\n\t\tprint_rule_consequence(&csq_buf, csq);\n\t\tsnprintf(wid, sizeof(wid), \"%i\", win);\n\t\tsetsid();\n\t\texecl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq_buf, NULL);\n\t\tfree(csq_buf);\n\t\terr(\"Couldn't spawn rule command.\\n\");\n\t} else if (pid > 0) {\n\t\tclose(fds[1]);\n\t\tpending_rule_t *pr = make_pending_rule(fds[0], win, csq);\n\t\tadd_pending_rule(pr);\n\t}\n\treturn (pid != -1);\n}\n\nvoid parse_rule_consequence(int fd, rule_consequence_t *csq)\n{\n\tif (fd == -1) {\n\t\treturn;\n\t}\n\tchar data[BUFSIZ];\n\tint nb;\n\twhile ((nb = read(fd, data, sizeof(data))) > 0) {\n\t\tint end = MIN(nb, (int) sizeof(data) - 1);\n\t\tdata[end] = '\\0';\n\t\tparse_keys_values(data, csq);\n\t}\n}\n\nvoid parse_key_value(char *key, char *value, rule_consequence_t *csq)\n{\n\tbool v;\n\tif (streq(\"monitor\", key)) {\n\t\tsnprintf(csq->monitor_desc, sizeof(csq->monitor_desc), \"%s\", value);\n\t} else if (streq(\"desktop\", key)) {\n\t\tsnprintf(csq->desktop_desc, sizeof(csq->desktop_desc), \"%s\", value);\n\t} else if (streq(\"node\", key)) {\n\t\tsnprintf(csq->node_desc, sizeof(csq->node_desc), \"%s\", value);\n\t} else if (streq(\"split_dir\", key)) {\n\t\tdirection_t dir;\n\t\tif (parse_direction(value, &dir)) {\n\t\t\tSET_CSQ_SPLIT_DIR(dir);\n\t\t}\n\t} else if (streq(\"state\", key)) {\n\t\tclient_state_t cst;\n\t\tif (parse_client_state(value, &cst)) {\n\t\t\tSET_CSQ_STATE(cst);\n\t\t}\n\t} else if (streq(\"layer\", key)) {\n\t\tstack_layer_t lyr;\n\t\tif (parse_stack_layer(value, &lyr)) {\n\t\t\tSET_CSQ_LAYER(lyr);\n\t\t}\n\t} else if (streq(\"split_ratio\", key)) {\n\t\tdouble rat;\n\t\tif (sscanf(value, \"%lf\", &rat) == 1 && rat > 0 && rat < 1) {\n\t\t\tcsq->split_ratio = rat;\n\t\t}\n\t} else if (streq(\"rectangle\", key)) {\n\t\tif (csq->rect == NULL) {\n\t\t\tcsq->rect = calloc(1, sizeof(xcb_rectangle_t));\n\t\t}\n\t\tif (!parse_rectangle(value, csq->rect)) {\n\t\t\tfree(csq->rect);\n\t\t\tcsq->rect = NULL;\n\t\t}\n\t} else if (streq(\"honor_size_hints\", key)) {\n\t\tif (!parse_honor_size_hints_mode(value, &csq->honor_size_hints)) {\n\t\t\tcsq->honor_size_hints = HONOR_SIZE_HINTS_DEFAULT;\n\t\t}\n\t} else if (parse_bool(value, &v)) {\n\t\tif (streq(\"hidden\", key)) {\n\t\t\tcsq->hidden = v;\n\t\t}\n#define SETCSQ(name) \\\n\t\telse if (streq(#name, key)) { \\\n\t\t\tcsq->name = v; \\\n\t\t}\n\t\tSETCSQ(sticky)\n\t\tSETCSQ(private)\n\t\tSETCSQ(locked)\n\t\tSETCSQ(marked)\n\t\tSETCSQ(center)\n\t\tSETCSQ(follow)\n\t\tSETCSQ(manage)\n\t\tSETCSQ(focus)\n\t\tSETCSQ(border)\n#undef SETCSQ\n\t}\n}\n\n#undef SET_CSQ_LAYER\n#undef SET_CSQ_STATE\n\nvoid list_rules(FILE *rsp)\n{\n\tfor (rule_t *r = rule_head; r != NULL; r = r->next) {\n\t\tfprintf(rsp, \"%s:%s:%s %c> %s\\n\", r->class_name, r->instance_name, r->name, r->one_shot?'-':'=', r->effect);\n\t}\n}\n"
  },
  {
    "path": "src/rule.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_RULE_H\n#define BSPWM_RULE_H\n\n#define MATCH_ANY  \"*\"\n#define CSQ_BLK    \" =,\\n\"\n\nrule_t *make_rule(void);\nvoid add_rule(rule_t *r);\nvoid remove_rule(rule_t *r);\nvoid remove_rule_by_cause(char *cause);\nbool remove_rule_by_index(int idx);\nrule_consequence_t *make_rule_consequence(void);\npending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);\nvoid add_pending_rule(pending_rule_t *pr);\nvoid remove_pending_rule(pending_rule_t *pr);\nvoid postpone_event(pending_rule_t *pr, xcb_generic_event_t *evt);\nevent_queue_t *make_event_queue(xcb_generic_event_t *evt);\nvoid _apply_window_type(xcb_window_t win, rule_consequence_t *csq);\nvoid _apply_window_state(xcb_window_t win, rule_consequence_t *csq);\nvoid _apply_transient(xcb_window_t win, rule_consequence_t *csq);\nvoid _apply_hints(xcb_window_t win, rule_consequence_t *csq);\nvoid _apply_class(xcb_window_t win, rule_consequence_t *csq);\nvoid _apply_name(xcb_window_t win, rule_consequence_t *csq);\nvoid parse_keys_values(char *buf, rule_consequence_t *csq);\nvoid apply_rules(xcb_window_t win, rule_consequence_t *csq);\nbool schedule_rules(xcb_window_t win, rule_consequence_t *csq);\nvoid parse_rule_consequence(int fd, rule_consequence_t *csq);\nvoid parse_key_value(char *key, char *value, rule_consequence_t *csq);\nvoid list_rules(FILE *rsp);\n\n#endif\n"
  },
  {
    "path": "src/settings.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include \"bspwm.h\"\n#include \"settings.h\"\n\nchar external_rules_command[MAXLEN];\nchar status_prefix[MAXLEN];\n\nchar normal_border_color[MAXLEN];\nchar active_border_color[MAXLEN];\nchar focused_border_color[MAXLEN];\nchar presel_feedback_color[MAXLEN];\n\npadding_t padding;\npadding_t monocle_padding;\nint window_gap;\nunsigned int border_width;\ndouble split_ratio;\nchild_polarity_t initial_polarity;\nautomatic_scheme_t automatic_scheme;\nbool removal_adjustment;\ntightness_t directional_focus_tightness;\n\nuint16_t pointer_modifier;\nuint32_t pointer_motion_interval;\npointer_action_t pointer_actions[3];\nint8_t mapping_events_count;\n\nbool presel_feedback;\nbool borderless_monocle;\nbool gapless_monocle;\nbool single_monocle;\nbool borderless_singleton;\n\nbool focus_follows_pointer;\nbool pointer_follows_focus;\nbool pointer_follows_monitor;\nint8_t click_to_focus;\nbool swallow_first_click;\nbool ignore_ewmh_focus;\nbool ignore_ewmh_struts;\nstate_transition_t ignore_ewmh_fullscreen;\n\nbool center_pseudo_tiled;\nhonor_size_hints_mode_t honor_size_hints;\n\nbool remove_disabled_monitors;\nbool remove_unplugged_monitors;\nbool merge_overlapping_monitors;\n\nvoid run_config(int run_level)\n{\n\tif (fork() == 0) {\n\t\tif (dpy != NULL) {\n\t\t\tclose(xcb_get_file_descriptor(dpy));\n\t\t}\n\t\tsetsid();\n\t\tchar arg1[2];\n\t\tsnprintf(arg1, 2, \"%i\", run_level);\n\t\texecl(config_path, config_path, arg1, (char *) NULL);\n\t\terr(\"Couldn't execute the configuration file.\\n\");\n\t}\n}\n\nvoid load_settings(void)\n{\n\tsnprintf(external_rules_command, sizeof(external_rules_command), \"%s\", EXTERNAL_RULES_COMMAND);\n\tsnprintf(status_prefix, sizeof(status_prefix), \"%s\", STATUS_PREFIX);\n\n\tsnprintf(normal_border_color, sizeof(normal_border_color), \"%s\", NORMAL_BORDER_COLOR);\n\tsnprintf(active_border_color, sizeof(active_border_color), \"%s\", ACTIVE_BORDER_COLOR);\n\tsnprintf(focused_border_color, sizeof(focused_border_color), \"%s\", FOCUSED_BORDER_COLOR);\n\tsnprintf(presel_feedback_color, sizeof(presel_feedback_color), \"%s\", PRESEL_FEEDBACK_COLOR);\n\n\tpadding = (padding_t) PADDING;\n\tmonocle_padding = (padding_t) MONOCLE_PADDING;\n\twindow_gap = WINDOW_GAP;\n\tborder_width = BORDER_WIDTH;\n\tsplit_ratio = SPLIT_RATIO;\n\tinitial_polarity = SECOND_CHILD;\n\tautomatic_scheme = AUTOMATIC_SCHEME;\n\tremoval_adjustment = REMOVAL_ADJUSTMENT;\n\tdirectional_focus_tightness = TIGHTNESS_HIGH;\n\n\tpointer_modifier = POINTER_MODIFIER;\n\tpointer_motion_interval = POINTER_MOTION_INTERVAL;\n\tpointer_actions[0] = ACTION_MOVE;\n\tpointer_actions[1] = ACTION_RESIZE_SIDE;\n\tpointer_actions[2] = ACTION_RESIZE_CORNER;\n\tmapping_events_count = MAPPING_EVENTS_COUNT;\n\n\tpresel_feedback = PRESEL_FEEDBACK;\n\tborderless_monocle = BORDERLESS_MONOCLE;\n\tgapless_monocle = GAPLESS_MONOCLE;\n\tsingle_monocle = SINGLE_MONOCLE;\n\tborderless_singleton = BORDERLESS_SINGLETON;\n\n\tfocus_follows_pointer = FOCUS_FOLLOWS_POINTER;\n\tpointer_follows_focus = POINTER_FOLLOWS_FOCUS;\n\tpointer_follows_monitor = POINTER_FOLLOWS_MONITOR;\n\tclick_to_focus = CLICK_TO_FOCUS;\n\tswallow_first_click = SWALLOW_FIRST_CLICK;\n\tignore_ewmh_focus = IGNORE_EWMH_FOCUS;\n\tignore_ewmh_fullscreen = IGNORE_EWMH_FULLSCREEN;\n\tignore_ewmh_struts = IGNORE_EWMH_STRUTS;\n\n\tcenter_pseudo_tiled = CENTER_PSEUDO_TILED;\n\thonor_size_hints = HONOR_SIZE_HINTS;\n\n\tremove_disabled_monitors = REMOVE_DISABLED_MONITORS;\n\tremove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;\n\tmerge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;\n}\n"
  },
  {
    "path": "src/settings.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_SETTINGS_H\n#define BSPWM_SETTINGS_H\n\n#include \"types.h\"\n\n#define POINTER_MODIFIER         XCB_MOD_MASK_4\n#define POINTER_MOTION_INTERVAL  17\n#define EXTERNAL_RULES_COMMAND   \"\"\n#define STATUS_PREFIX            \"W\"\n\n#define NORMAL_BORDER_COLOR           \"#30302f\"\n#define ACTIVE_BORDER_COLOR           \"#474645\"\n#define FOCUSED_BORDER_COLOR          \"#817f7f\"\n#define PRESEL_FEEDBACK_COLOR         \"#f4d775\"\n\n#define PADDING              {0, 0, 0, 0}\n#define MONOCLE_PADDING      {0, 0, 0, 0}\n#define WINDOW_GAP           6\n#define BORDER_WIDTH         1\n#define SPLIT_RATIO          0.5\n#define AUTOMATIC_SCHEME     SCHEME_LONGEST_SIDE\n#define REMOVAL_ADJUSTMENT   true\n\n#define PRESEL_FEEDBACK             true\n#define BORDERLESS_MONOCLE          false\n#define GAPLESS_MONOCLE             false\n#define SINGLE_MONOCLE              false\n#define BORDERLESS_SINGLETON        false\n\n#define FOCUS_FOLLOWS_POINTER       false\n#define POINTER_FOLLOWS_FOCUS       false\n#define POINTER_FOLLOWS_MONITOR     false\n#define CLICK_TO_FOCUS              XCB_BUTTON_INDEX_1\n#define SWALLOW_FIRST_CLICK         false\n#define IGNORE_EWMH_FOCUS           false\n#define IGNORE_EWMH_FULLSCREEN      0\n#define IGNORE_EWMH_STRUTS          false\n\n#define CENTER_PSEUDO_TILED         true\n#define HONOR_SIZE_HINTS            HONOR_SIZE_HINTS_NO\n#define MAPPING_EVENTS_COUNT        1\n\n#define REMOVE_DISABLED_MONITORS    false\n#define REMOVE_UNPLUGGED_MONITORS   false\n#define MERGE_OVERLAPPING_MONITORS  false\n\nextern char external_rules_command[MAXLEN];\nextern char status_prefix[MAXLEN];\n\nextern char normal_border_color[MAXLEN];\nextern char active_border_color[MAXLEN];\nextern char focused_border_color[MAXLEN];\nextern char presel_feedback_color[MAXLEN];\n\nextern padding_t padding;\nextern padding_t monocle_padding;\nextern int window_gap;\nextern unsigned int border_width;\nextern double split_ratio;\nextern child_polarity_t initial_polarity;\nextern automatic_scheme_t automatic_scheme;\nextern bool removal_adjustment;\nextern tightness_t directional_focus_tightness;\n\nextern uint16_t pointer_modifier;\nextern uint32_t pointer_motion_interval;\nextern pointer_action_t pointer_actions[3];\nextern int8_t mapping_events_count;\n\nextern bool presel_feedback;\nextern bool borderless_monocle;\nextern bool gapless_monocle;\nextern bool single_monocle;\nextern bool borderless_singleton;\n\nextern bool focus_follows_pointer;\nextern bool pointer_follows_focus;\nextern bool pointer_follows_monitor;\nextern int8_t click_to_focus;\nextern bool swallow_first_click;\nextern bool ignore_ewmh_focus;\nextern bool ignore_ewmh_struts;\nextern state_transition_t ignore_ewmh_fullscreen;\n\nextern bool center_pseudo_tiled;\nextern honor_size_hints_mode_t honor_size_hints;\n\nextern bool remove_disabled_monitors;\nextern bool remove_unplugged_monitors;\nextern bool merge_overlapping_monitors;\n\nvoid run_config(int run_level);\nvoid load_settings(void);\n\n#endif\n"
  },
  {
    "path": "src/stack.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdlib.h>\n#include \"bspwm.h\"\n#include \"window.h\"\n#include \"subscribe.h\"\n#include \"ewmh.h\"\n#include \"tree.h\"\n#include \"stack.h\"\n\nstacking_list_t *make_stack(node_t *n)\n{\n\tstacking_list_t *s = calloc(1, sizeof(stacking_list_t));\n\ts->node = n;\n\ts->prev = s->next = NULL;\n\treturn s;\n}\n\nvoid stack_insert_after(stacking_list_t *a, node_t *n)\n{\n\tstacking_list_t *s = make_stack(n);\n\tif (a == NULL) {\n\t\tstack_head = stack_tail = s;\n\t} else {\n\t\tif (a->node == n) {\n\t\t\tfree(s);\n\t\t\treturn;\n\t\t}\n\t\tremove_stack_node(n);\n\t\tstacking_list_t *b = a->next;\n\t\tif (b != NULL) {\n\t\t\tb->prev = s;\n\t\t}\n\t\ts->next = b;\n\t\ts->prev = a;\n\t\ta->next = s;\n\t\tif (stack_tail == a) {\n\t\t\tstack_tail = s;\n\t\t}\n\t}\n}\n\nvoid stack_insert_before(stacking_list_t *a, node_t *n)\n{\n\tstacking_list_t *s = make_stack(n);\n\tif (a == NULL) {\n\t\tstack_head = stack_tail = s;\n\t} else {\n\t\tif (a->node == n) {\n\t\t\tfree(s);\n\t\t\treturn;\n\t\t}\n\t\tremove_stack_node(n);\n\t\tstacking_list_t *b = a->prev;\n\t\tif (b != NULL) {\n\t\t\tb->next = s;\n\t\t}\n\t\ts->prev = b;\n\t\ts->next = a;\n\t\ta->prev = s;\n\t\tif (stack_head == a) {\n\t\t\tstack_head = s;\n\t\t}\n\t}\n}\n\nvoid remove_stack(stacking_list_t *s)\n{\n\tif (s == NULL) {\n\t\treturn;\n\t}\n\tstacking_list_t *a = s->prev;\n\tstacking_list_t *b = s->next;\n\tif (a != NULL) {\n\t\ta->next = b;\n\t}\n\tif (b != NULL) {\n\t\tb->prev = a;\n\t}\n\tif (s == stack_head) {\n\t\tstack_head = b;\n\t}\n\tif (s == stack_tail) {\n\t\tstack_tail = a;\n\t}\n\tfree(s);\n}\n\nvoid remove_stack_node(node_t *n)\n{\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tfor (stacking_list_t *s = stack_head; s != NULL; s = s->next) {\n\t\t\tif (s->node == f) {\n\t\t\t\tremove_stack(s);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nint stack_level(client_t *c)\n{\n\tint layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2));\n\tint state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 1 : 2));\n\treturn 3 * layer_level + state_level;\n}\n\nint stack_cmp(client_t *c1, client_t *c2)\n{\n\treturn stack_level(c1) - stack_level(c2);\n}\n\nstacking_list_t *limit_above(node_t *n)\n{\n\tstacking_list_t *s = stack_head;\n\twhile (s != NULL && stack_cmp(n->client, s->node->client) >= 0) {\n\t\ts = s->next;\n\t}\n\tif (s == NULL) {\n\t\ts = stack_tail;\n\t}\n\tif (s->node == n) {\n\t\ts = s->prev;\n\t}\n\treturn s;\n}\n\nstacking_list_t *limit_below(node_t *n)\n{\n\tstacking_list_t *s = stack_tail;\n\twhile (s != NULL && stack_cmp(n->client, s->node->client) <= 0) {\n\t\ts = s->prev;\n\t}\n\tif (s == NULL) {\n\t\ts = stack_head;\n\t}\n\tif (s->node == n) {\n\t\ts = s->next;\n\t}\n\treturn s;\n}\n\nvoid stack(desktop_t *d, node_t *n, bool focused)\n{\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (stack_head == NULL) {\n\t\t\tstack_insert_after(NULL, f);\n\t\t} else {\n\t\t\tstacking_list_t *s = (focused ? limit_above(f) : limit_below(f));\n\t\t\tif (s == NULL) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint i = stack_cmp(f->client, s->node->client);\n\t\t\tif (i < 0 || (i == 0 && !focused)) {\n\t\t\t\tstack_insert_before(s, f);\n\t\t\t\twindow_below(f->id, s->node->id);\n\t\t\t\tput_status(SBSC_MASK_NODE_STACK, \"node_stack 0x%08X below 0x%08X\\n\", f->id, s->node->id);\n\t\t\t} else {\n\t\t\t\tstack_insert_after(s, f);\n\t\t\t\twindow_above(f->id, s->node->id);\n\t\t\t\tput_status(SBSC_MASK_NODE_STACK, \"node_stack 0x%08X above 0x%08X\\n\", f->id, s->node->id);\n\t\t\t}\n\t\t}\n\t}\n\n\tewmh_update_client_list(true);\n\trestack_presel_feedbacks(d);\n}\n\nvoid restack_presel_feedbacks(desktop_t *d)\n{\n\tstacking_list_t *s = stack_tail;\n\twhile (s != NULL && !IS_TILED(s->node->client)) {\n\t\ts = s->prev;\n\t}\n\tif (s != NULL) {\n\t\trestack_presel_feedbacks_in(d->root, s->node);\n\t}\n}\n\nvoid restack_presel_feedbacks_in(node_t *r, node_t *n)\n{\n\tif (r == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (r->presel != NULL) {\n\t\t\twindow_above(r->presel->feedback, n->id);\n\t\t}\n\t\trestack_presel_feedbacks_in(r->first_child, n);\n\t\trestack_presel_feedbacks_in(r->second_child, n);\n\t}\n}\n"
  },
  {
    "path": "src/stack.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_STACK_H\n#define BSPWM_STACK_H\n\nstacking_list_t *make_stack(node_t *n);\nvoid stack_insert_after(stacking_list_t *a, node_t *n);\nvoid stack_insert_before(stacking_list_t *a, node_t *n);\nvoid remove_stack(stacking_list_t *s);\nvoid remove_stack_node(node_t *n);\nint stack_level(client_t *c);\nint stack_cmp(client_t *c1, client_t *c2);\nstacking_list_t *limit_above(node_t *n);\nstacking_list_t *limit_below(node_t *n);\nvoid stack(desktop_t *d, node_t *n, bool focused);\nvoid restack_presel_feedbacks(desktop_t *d);\nvoid restack_presel_feedbacks_in(node_t *r, node_t *n);\n\n#endif\n"
  },
  {
    "path": "src/subscribe.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <ctype.h>\n#include <stdarg.h>\n#include <fcntl.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"settings.h\"\n#include \"subscribe.h\"\n#include \"tree.h\"\n\nsubscriber_list_t *make_subscriber(FILE *stream, char *fifo_path, int field, int count)\n{\n\tsubscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));\n\tsb->prev = sb->next = NULL;\n\tsb->stream = stream;\n\tsb->fifo_path = fifo_path;\n\tsb->field = field;\n\tsb->count = count;\n\treturn sb;\n}\n\nvoid remove_subscriber(subscriber_list_t *sb)\n{\n\tif (sb == NULL) {\n\t\treturn;\n\t}\n\tsubscriber_list_t *a = sb->prev;\n\tsubscriber_list_t *b = sb->next;\n\tif (a != NULL) {\n\t\ta->next = b;\n\t}\n\tif (b != NULL) {\n\t\tb->prev = a;\n\t}\n\tif (sb == subscribe_head) {\n\t\tsubscribe_head = b;\n\t}\n\tif (sb == subscribe_tail) {\n\t\tsubscribe_tail = a;\n\t}\n\tif (restart) {\n\t\tint cli_fd = fileno(sb->stream);\n\t\tfcntl(cli_fd, F_SETFD, ~FD_CLOEXEC & fcntl(cli_fd, F_GETFD));\n\t} else {\n\t\tfclose(sb->stream);\n\t\tunlink(sb->fifo_path);\n\t}\n\tfree(sb->fifo_path);\n\tfree(sb);\n}\n\nvoid add_subscriber(subscriber_list_t *sb)\n{\n\tif (subscribe_head == NULL) {\n\t\tsubscribe_head = subscribe_tail = sb;\n\t} else {\n\t\tsubscribe_tail->next = sb;\n\t\tsb->prev = subscribe_tail;\n\t\tsubscribe_tail = sb;\n\t}\n\tint cli_fd = fileno(sb->stream);\n\tfcntl(cli_fd, F_SETFD, FD_CLOEXEC | fcntl(cli_fd, F_GETFD));\n\tif (sb->field & SBSC_MASK_REPORT) {\n\t\tprint_report(sb->stream);\n\t\tif (sb->count-- == 1) {\n\t\t\tremove_subscriber(sb);\n\t\t}\n\t}\n}\n\nint print_report(FILE *stream)\n{\n\tfprintf(stream, \"%s\", status_prefix);\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfprintf(stream, \"%c%s\", (mon == m ? 'M' : 'm'), m->name);\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tchar c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));\n\t\t\tif (m->desk == d) {\n\t\t\t\tc = toupper(c);\n\t\t\t}\n\t\t\tfprintf(stream, \":%c%s\", c, d->name);\n\t\t}\n\t\tif (m->desk != NULL) {\n\t\t\tfprintf(stream, \":L%c\", LAYOUT_CHR(m->desk->layout));\n\t\t\tif (m->desk->focus != NULL) {\n\t\t\t\tnode_t *n = m->desk->focus;\n\t\t\t\tif (n->client != NULL) {\n\t\t\t\t\tfprintf(stream, \":T%c\", STATE_CHR(n->client->state));\n\t\t\t\t} else {\n\t\t\t\t\tfprintf(stream, \":T@\");\n\t\t\t\t}\n\t\t\t\tint i = 0;\n\t\t\t\tchar flags[5];\n\t\t\t\tif (n->sticky) {\n\t\t\t\t\tflags[i++] = 'S';\n\t\t\t\t}\n\t\t\t\tif (n->private) {\n\t\t\t\t\tflags[i++] = 'P';\n\t\t\t\t}\n\t\t\t\tif (n->locked) {\n\t\t\t\t\tflags[i++] = 'L';\n\t\t\t\t}\n\t\t\t\tif (n->marked) {\n\t\t\t\t\tflags[i++] = 'M';\n\t\t\t\t}\n\t\t\t\tflags[i] = '\\0';\n\t\t\t\tfprintf(stream, \":G%s\", flags);\n\t\t\t}\n\t\t}\n\t\tif (m != mon_tail) {\n\t\t\tfprintf(stream, \"%s\", \":\");\n\t\t}\n\t}\n\tfprintf(stream, \"%s\", \"\\n\");\n\treturn fflush(stream);\n}\n\nvoid put_status(subscriber_mask_t mask, ...)\n{\n\tsubscriber_list_t *sb = subscribe_head;\n\tint ret;\n\twhile (sb != NULL) {\n\t\tsubscriber_list_t *next = sb->next;\n\t\tif (sb->field & mask) {\n\t\t\tif (sb->count > 0) {\n\t\t\t\tsb->count--;\n\t\t\t}\n\t\t\tif (mask == SBSC_MASK_REPORT) {\n\t\t\t\tret = print_report(sb->stream);\n\t\t\t} else {\n\t\t\t\tchar *fmt;\n\t\t\t\tva_list args;\n\t\t\t\tva_start(args, mask);\n\t\t\t\tfmt = va_arg(args, char *);\n\t\t\t\tvfprintf(sb->stream, fmt, args);\n\t\t\t\tva_end(args);\n\t\t\t\tret = fflush(sb->stream);\n\t\t\t}\n\t\t\tif (ret != 0 || sb->count == 0) {\n\t\t\t\tremove_subscriber(sb);\n\t\t\t}\n\t\t}\n\t\tsb = next;\n\t}\n}\n\nvoid prune_dead_subscribers(void)\n{\n\tsubscriber_list_t *sb = subscribe_head;\n\twhile (sb != NULL) {\n\t\tsubscriber_list_t *next = sb->next;\n\t\t// Is the subscriber's stream closed?\n\t\tif (write(fileno(sb->stream), NULL, 0) == -1) {\n\t\t\tremove_subscriber(sb);\n\t\t}\n\t\tsb = next;\n\t}\n}\n"
  },
  {
    "path": "src/subscribe.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_SUBSCRIBE_H\n#define BSPWM_SUBSCRIBE_H\n\n#define FIFO_TEMPLATE  \"bspwm_fifo.XXXXXX\"\n\ntypedef enum {\n\tSBSC_MASK_REPORT = 1 << 0,\n\tSBSC_MASK_MONITOR_ADD = 1 << 1,\n\tSBSC_MASK_MONITOR_RENAME = 1 << 2,\n\tSBSC_MASK_MONITOR_REMOVE = 1 << 3,\n\tSBSC_MASK_MONITOR_SWAP = 1 << 4,\n\tSBSC_MASK_MONITOR_FOCUS = 1 << 5,\n\tSBSC_MASK_MONITOR_GEOMETRY = 1 << 6,\n\tSBSC_MASK_DESKTOP_ADD = 1 << 7,\n\tSBSC_MASK_DESKTOP_RENAME = 1 << 8,\n\tSBSC_MASK_DESKTOP_REMOVE = 1 << 9,\n\tSBSC_MASK_DESKTOP_SWAP = 1 << 10,\n\tSBSC_MASK_DESKTOP_TRANSFER = 1 << 11,\n\tSBSC_MASK_DESKTOP_FOCUS = 1 << 12,\n\tSBSC_MASK_DESKTOP_ACTIVATE = 1 << 13,\n\tSBSC_MASK_DESKTOP_LAYOUT = 1 << 14,\n\tSBSC_MASK_NODE_ADD = 1 << 15,\n\tSBSC_MASK_NODE_REMOVE = 1 << 16,\n\tSBSC_MASK_NODE_SWAP = 1 << 17,\n\tSBSC_MASK_NODE_TRANSFER = 1 << 18,\n\tSBSC_MASK_NODE_FOCUS = 1 << 19,\n\tSBSC_MASK_NODE_PRESEL = 1 << 20,\n\tSBSC_MASK_NODE_STACK = 1 << 21,\n\tSBSC_MASK_NODE_ACTIVATE = 1 << 22,\n\tSBSC_MASK_NODE_GEOMETRY = 1 << 23,\n\tSBSC_MASK_NODE_STATE = 1 << 24,\n\tSBSC_MASK_NODE_FLAG = 1 << 25,\n\tSBSC_MASK_NODE_LAYER = 1 << 26,\n\tSBSC_MASK_POINTER_ACTION = 1 << 27,\n\tSBSC_MASK_MONITOR = (1 << 7) - (1 << 1),\n\tSBSC_MASK_DESKTOP = (1 << 15) - (1 << 7),\n\tSBSC_MASK_NODE = (1 << 27) - (1 << 15),\n\tSBSC_MASK_ALL = (1 << 28) - 1\n} subscriber_mask_t;\n\nsubscriber_list_t *make_subscriber(FILE *stream, char *fifo_path, int field, int count);\nvoid remove_subscriber(subscriber_list_t *sb);\nvoid add_subscriber(subscriber_list_t *sb);\nint print_report(FILE *stream);\nvoid put_status(subscriber_mask_t mask, ...);\n\n/* Remove any subscriber for which the stream has been closed and is no longer\n * writable. */\nvoid prune_dead_subscribers(void);\n\n#endif\n"
  },
  {
    "path": "src/tree.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <limits.h>\n#include \"bspwm.h\"\n#include \"desktop.h\"\n#include \"ewmh.h\"\n#include \"history.h\"\n#include \"monitor.h\"\n#include \"query.h\"\n#include \"geometry.h\"\n#include \"subscribe.h\"\n#include \"settings.h\"\n#include \"pointer.h\"\n#include \"stack.h\"\n#include \"window.h\"\n#include \"tree.h\"\n\nvoid arrange(monitor_t *m, desktop_t *d)\n{\n\tif (d->root == NULL) {\n\t\treturn;\n\t}\n\n\txcb_rectangle_t rect = m->rectangle;\n\n\trect.x += m->padding.left + d->padding.left;\n\trect.y += m->padding.top + d->padding.top;\n\trect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right;\n\trect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom;\n\n\tif (d->layout == LAYOUT_MONOCLE) {\n\t\trect.x += monocle_padding.left;\n\t\trect.y += monocle_padding.top;\n\t\trect.width -= monocle_padding.left + monocle_padding.right;\n\t\trect.height -= monocle_padding.top + monocle_padding.bottom;\n\t}\n\n\tif (!gapless_monocle || d->layout != LAYOUT_MONOCLE) {\n\t\trect.x += d->window_gap;\n\t\trect.y += d->window_gap;\n\t\trect.width -= d->window_gap;\n\t\trect.height -= d->window_gap;\n\t}\n\n\tapply_layout(m, d, d->root, rect, rect);\n}\n\nvoid apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tn->rectangle = rect;\n\n\tif (n->presel != NULL) {\n\t\tdraw_presel_feedback(m, d, n);\n\t}\n\n\tif (is_leaf(n)) {\n\n\t\tif (n->client == NULL) {\n\t\t\treturn;\n\t\t}\n\n\t\tunsigned int bw;\n\t\tbool the_only_window = !m->prev && !m->next && d->root->client;\n\t\tif ((borderless_monocle && d->layout == LAYOUT_MONOCLE && IS_TILED(n->client))\n\t\t    || (borderless_singleton && the_only_window)\n\t\t    || n->client->state == STATE_FULLSCREEN) {\n\t\t\tbw = 0;\n\t\t} else {\n\t\t\tbw = n->client->border_width;\n\t\t}\n\n\t\txcb_rectangle_t r;\n\t\txcb_rectangle_t cr = get_window_rectangle(n);\n\t\tclient_state_t s = n->client->state;\n\t\t/* tiled and pseudo-tiled clients */\n\t\tif (s == STATE_TILED || s == STATE_PSEUDO_TILED) {\n\t\t\tint wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);\n\t\t\tr = rect;\n\t\t\tint bleed = wg + 2 * bw;\n\t\t\tr.width = (bleed < r.width ? r.width - bleed : 1);\n\t\t\tr.height = (bleed < r.height ? r.height - bleed : 1);\n\t\t\t/* pseudo-tiled clients */\n\t\t\tif (s == STATE_PSEUDO_TILED) {\n\t\t\t\txcb_rectangle_t f = n->client->floating_rectangle;\n\t\t\t\tr.width = MIN(r.width, f.width);\n\t\t\t\tr.height = MIN(r.height, f.height);\n\t\t\t\tif (center_pseudo_tiled) {\n\t\t\t\t\tr.x = rect.x - bw + (rect.width - wg - r.width) / 2;\n\t\t\t\t\tr.y = rect.y - bw + (rect.height - wg - r.height) / 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tn->client->tiled_rectangle = r;\n\t\t/* floating clients */\n\t\t} else if (s == STATE_FLOATING) {\n\t\t\tr = n->client->floating_rectangle;\n\t\t/* fullscreen clients */\n\t\t} else {\n\t\t\tr = m->rectangle;\n\t\t\tn->client->tiled_rectangle = r;\n\t\t}\n\n\t\tapply_size_hints(n->client, &r.width, &r.height);\n\n\t\tif (!rect_eq(r, cr)) {\n\t\t\twindow_move_resize(n->id, r.x, r.y, r.width, r.height);\n\t\t\tif (!grabbing) {\n\t\t\t\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", m->id, d->id, n->id, r.width, r.height, r.x, r.y);\n\t\t\t}\n\t\t}\n\n\t\twindow_border_width(n->id, bw);\n\n\t} else {\n\t\txcb_rectangle_t first_rect;\n\t\txcb_rectangle_t second_rect;\n\n\t\tif (d->layout == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {\n\t\t\tfirst_rect = second_rect = rect;\n\t\t} else {\n\t\t\tunsigned int fence;\n\t\t\tif (n->split_type == TYPE_VERTICAL) {\n\t\t\t\tfence = rect.width * n->split_ratio;\n\t\t\t\tif ((n->first_child->constraints.min_width + n->second_child->constraints.min_width) <= rect.width) {\n\t\t\t\t\tif (fence < n->first_child->constraints.min_width) {\n\t\t\t\t\t\tfence = n->first_child->constraints.min_width;\n\t\t\t\t\t\tn->split_ratio = (double) fence / (double) rect.width;\n\t\t\t\t\t} else if (fence > (uint16_t) (rect.width - n->second_child->constraints.min_width)) {\n\t\t\t\t\t\tfence = (rect.width - n->second_child->constraints.min_width);\n\t\t\t\t\t\tn->split_ratio = (double) fence / (double) rect.width;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfirst_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};\n\t\t\t\tsecond_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};\n\t\t\t} else {\n\t\t\t\tfence = rect.height * n->split_ratio;\n\t\t\t\tif ((n->first_child->constraints.min_height + n->second_child->constraints.min_height) <= rect.height) {\n\t\t\t\t\tif (fence < n->first_child->constraints.min_height) {\n\t\t\t\t\t\tfence = n->first_child->constraints.min_height;\n\t\t\t\t\t\tn->split_ratio = (double) fence / (double) rect.height;\n\t\t\t\t\t} else if (fence > (uint16_t) (rect.height - n->second_child->constraints.min_height)) {\n\t\t\t\t\t\tfence = (rect.height - n->second_child->constraints.min_height);\n\t\t\t\t\t\tn->split_ratio = (double) fence / (double) rect.height;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfirst_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};\n\t\t\t\tsecond_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};\n\t\t\t}\n\t\t}\n\n\t\tapply_layout(m, d, n->first_child, first_rect, root_rect);\n\t\tapply_layout(m, d, n->second_child, second_rect, root_rect);\n\t}\n}\n\npresel_t *make_presel(void)\n{\n\tpresel_t *p = calloc(1, sizeof(presel_t));\n\tp->split_dir = DIR_EAST;\n\tp->split_ratio = split_ratio;\n\tp->feedback = XCB_NONE;\n\treturn p;\n}\n\nbool set_type(node_t *n, split_type_t typ)\n{\n\tif (n == NULL || n->split_type == typ) {\n\t\treturn false;\n\t}\n\n\tn->split_type = typ;\n\tupdate_constraints(n);\n\trebuild_constraints_towards_root(n);\n\treturn true;\n}\n\nbool set_ratio(node_t *n, double rat)\n{\n\tif (n == NULL || n->split_ratio == rat) {\n\t\treturn false;\n\t}\n\n\tn->split_ratio = rat;\n\treturn true;\n}\n\nvoid presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)\n{\n\tif (n->presel == NULL) {\n\t\tn->presel = make_presel();\n\t}\n\n\tn->presel->split_dir = dir;\n\n\tput_status(SBSC_MASK_NODE_PRESEL, \"node_presel 0x%08X 0x%08X 0x%08X dir %s\\n\", m->id, d->id, n->id, SPLIT_DIR_STR(dir));\n}\n\nvoid presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)\n{\n\tif (n->presel == NULL) {\n\t\tn->presel = make_presel();\n\t}\n\n\tn->presel->split_ratio = ratio;\n\n\tput_status(SBSC_MASK_NODE_PRESEL, \"node_presel 0x%08X 0x%08X 0x%08X ratio %lf\\n\", m->id, d->id, n->id, ratio);\n}\n\nvoid cancel_presel(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n->presel == NULL) {\n\t\treturn;\n\t}\n\n\tif (n->presel->feedback != XCB_NONE) {\n\t\txcb_destroy_window(dpy, n->presel->feedback);\n\t}\n\n\tfree(n->presel);\n\tn->presel = NULL;\n\n\tput_status(SBSC_MASK_NODE_PRESEL, \"node_presel 0x%08X 0x%08X 0x%08X cancel\\n\", m->id, d->id, n->id);\n}\n\nvoid cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\tcancel_presel(m, d, n);\n\tcancel_presel_in(m, d, n->first_child);\n\tcancel_presel_in(m, d, n->second_child);\n}\n\nnode_t *find_public(desktop_t *d)\n{\n\tunsigned int b_manual_area = 0;\n\tunsigned int b_automatic_area = 0;\n\tnode_t *b_manual = NULL;\n\tnode_t *b_automatic = NULL;\n\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\tif (n->vacant) {\n\t\t\tcontinue;\n\t\t}\n\t\tunsigned int n_area = node_area(d, n);\n\t\tif (n_area > b_manual_area && (n->presel != NULL || !n->private)) {\n\t\t\tb_manual = n;\n\t\t\tb_manual_area = n_area;\n\t\t}\n\t\tif (n_area > b_automatic_area &&\n\t\t    n->presel == NULL && !n->private && private_count(n->parent) == 0) {\n\t\t\tb_automatic = n;\n\t\t\tb_automatic_area = n_area;\n\t\t}\n\t}\n\tif (b_automatic != NULL) {\n\t\treturn b_automatic;\n\t} else {\n\t\treturn b_manual;\n\t}\n}\n\nnode_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)\n{\n\tif (d == NULL || n == NULL) {\n\t\treturn NULL;\n\t}\n\n\tbool d_was_not_occupied = d->root == NULL;\n\n\t/* n: inserted node */\n\t/* c: new internal node */\n\t/* f: focus or insertion anchor */\n\t/* p: parent of focus */\n\t/* g: grand parent of focus */\n\n\tif (f == NULL) {\n\t\tf = d->root;\n\t}\n\n\tif (f == NULL) {\n\t\td->root = n;\n\t} else if (IS_RECEPTACLE(f) && f->presel == NULL) {\n\t\tnode_t *p = f->parent;\n\t\tif (p != NULL) {\n\t\t\tif (is_first_child(f)) {\n\t\t\t\tp->first_child = n;\n\t\t\t} else {\n\t\t\t\tp->second_child = n;\n\t\t\t}\n\t\t} else {\n\t\t\td->root = n;\n\t\t}\n\t\tn->parent = p;\n\t\tfree(f);\n\t\tf = NULL;\n\t} else {\n\t\tnode_t *c = make_node(XCB_NONE);\n\t\tnode_t *p = f->parent;\n\t\tif (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {\n\t\t\tnode_t *k = find_public(d);\n\t\t\tif (k != NULL) {\n\t\t\t\tf = k;\n\t\t\t\tp = f->parent;\n\t\t\t}\n\t\t\tif (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {\n\t\t\t\txcb_rectangle_t rect = get_rectangle(m, d, f);\n\t\t\t\tpresel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));\n\t\t\t}\n\t\t}\n\t\tn->parent = c;\n\t\tif (f->presel == NULL) {\n\t\t\tbool single_tiled = f->client != NULL && IS_TILED(f->client) && tiled_count(d->root, true) == 1;\n\t\t\tif (p == NULL || automatic_scheme != SCHEME_SPIRAL || single_tiled) {\n\t\t\t\tif (p != NULL) {\n\t\t\t\t\tif (is_first_child(f)) {\n\t\t\t\t\t\tp->first_child = c;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tp->second_child = c;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\td->root = c;\n\t\t\t\t}\n\t\t\t\tc->parent = p;\n\t\t\t\tf->parent = c;\n\t\t\t\tif (initial_polarity == FIRST_CHILD) {\n\t\t\t\t\tc->first_child = n;\n\t\t\t\t\tc->second_child = f;\n\t\t\t\t} else {\n\t\t\t\t\tc->first_child = f;\n\t\t\t\t\tc->second_child = n;\n\t\t\t\t}\n\t\t\t\tif (p == NULL || automatic_scheme == SCHEME_LONGEST_SIDE || single_tiled) {\n\t\t\t\t\tif (f->rectangle.width > f->rectangle.height) {\n\t\t\t\t\t\tc->split_type = TYPE_VERTICAL;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tc->split_type = TYPE_HORIZONTAL;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnode_t *q = p;\n\t\t\t\t\twhile (q != NULL && (q->first_child->vacant || q->second_child->vacant)) {\n\t\t\t\t\t\tq = q->parent;\n\t\t\t\t\t}\n\t\t\t\t\tif (q == NULL) {\n\t\t\t\t\t\tq = p;\n\t\t\t\t\t}\n\t\t\t\t\tif (q->split_type == TYPE_HORIZONTAL) {\n\t\t\t\t\t\tc->split_type = TYPE_VERTICAL;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tc->split_type = TYPE_HORIZONTAL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnode_t *g = p->parent;\n\t\t\t\tc->parent = g;\n\t\t\t\tif (g != NULL) {\n\t\t\t\t\tif (is_first_child(p)) {\n\t\t\t\t\t\tg->first_child = c;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tg->second_child = c;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\td->root = c;\n\t\t\t\t}\n\t\t\t\tc->split_type = p->split_type;\n\t\t\t\tc->split_ratio = p->split_ratio;\n\t\t\t\tp->parent = c;\n\t\t\t\tint rot;\n\t\t\t\tif (is_first_child(f)) {\n\t\t\t\t\tc->first_child = n;\n\t\t\t\t\tc->second_child = p;\n\t\t\t\t\trot = 90;\n\t\t\t\t} else {\n\t\t\t\t\tc->first_child = p;\n\t\t\t\t\tc->second_child = n;\n\t\t\t\t\trot = 270;\n\t\t\t\t}\n\t\t\t\tif (!n->vacant) {\n\t\t\t\t\trotate_tree(p, rot);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (p != NULL) {\n\t\t\t\tif (is_first_child(f)) {\n\t\t\t\t\tp->first_child = c;\n\t\t\t\t} else {\n\t\t\t\t\tp->second_child = c;\n\t\t\t\t}\n\t\t\t}\n\t\t\tc->split_ratio = f->presel->split_ratio;\n\t\t\tc->parent = p;\n\t\t\tf->parent = c;\n\t\t\tswitch (f->presel->split_dir) {\n\t\t\t\tcase DIR_WEST:\n\t\t\t\t\tc->split_type = TYPE_VERTICAL;\n\t\t\t\t\tc->first_child = n;\n\t\t\t\t\tc->second_child = f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_EAST:\n\t\t\t\t\tc->split_type = TYPE_VERTICAL;\n\t\t\t\t\tc->first_child = f;\n\t\t\t\t\tc->second_child = n;\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_NORTH:\n\t\t\t\t\tc->split_type = TYPE_HORIZONTAL;\n\t\t\t\t\tc->first_child = n;\n\t\t\t\t\tc->second_child = f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase DIR_SOUTH:\n\t\t\t\t\tc->split_type = TYPE_HORIZONTAL;\n\t\t\t\t\tc->first_child = f;\n\t\t\t\t\tc->second_child = n;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (d->root == f) {\n\t\t\t\td->root = c;\n\t\t\t}\n\t\t\tcancel_presel(m, d, f);\n\t\t\tset_marked(m, d, n, false);\n\t\t}\n\t}\n\n\tpropagate_flags_upward(m, d, n);\n\n\tif (d->focus == NULL && is_focusable(n)) {\n\t\td->focus = n;\n\t}\n\n\tif (d_was_not_occupied) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n\n\treturn f;\n}\n\nvoid insert_receptacle(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tnode_t *r = make_node(XCB_NONE);\n\tinsert_node(m, d, r, n);\n\tput_status(SBSC_MASK_NODE_ADD, \"node_add 0x%08X 0x%08X 0x%08X 0x%08X\\n\", m->id, d->id, n != NULL ? n->id : 0, r->id);\n\n\tif (single_monocle && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) {\n\t\tset_layout(m, d, d->user_layout, false);\n\t}\n}\n\nbool activate_node(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL && d->root != NULL) {\n\t\tn = d->focus;\n\t\tif (n == NULL) {\n\t\t\tn = history_last_node(d, NULL);\n\t\t}\n\t\tif (n == NULL) {\n\t\t\tn = first_focusable_leaf(d->root);\n\t\t}\n\t}\n\n\tif (d == mon->desk || (n != NULL && !is_focusable(n))) {\n\t\treturn false;\n\t}\n\n\tif (n != NULL) {\n\t\tif (d->focus != NULL && n != d->focus) {\n\t\t\tneutralize_occluding_windows(m, d, n);\n\t\t}\n\t\tstack(d, n, true);\n\t\tif (d->focus != n) {\n\t\t\tfor (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {\n\t\t\t\tif (f->client != NULL && !is_descendant(f, n)) {\n\t\t\t\t\twindow_draw_border(f->id, get_border_color(false, (m == mon)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdraw_border(n, true, (m == mon));\n\t}\n\n\td->focus = n;\n\thistory_add(m, d, n, false);\n\n\tput_status(SBSC_MASK_REPORT);\n\n\tif (n == NULL) {\n\t\treturn true;\n\t}\n\n\tput_status(SBSC_MASK_NODE_ACTIVATE, \"node_activate 0x%08X 0x%08X 0x%08X\\n\", m->id, d->id, n->id);\n\n\treturn true;\n}\n\nvoid transfer_sticky_nodes(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else if (n->sticky) {\n\t\tsticky_still = false;\n\t\ttransfer_node(ms, ds, n, md, dd, dd->focus, false);\n\t\tsticky_still = true;\n\t} else {\n\t\t/* we need references to the children because n might be freed after\n\t\t * the first recursive call */\n\t\tnode_t *first_child = n->first_child;\n\t\tnode_t *second_child = n->second_child;\n\t\ttransfer_sticky_nodes(ms, ds, md, dd, first_child);\n\t\ttransfer_sticky_nodes(ms, ds, md, dd, second_child);\n\t}\n}\n\nbool focus_node(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (m == NULL) {\n\t\tm = mon;\n\t\tif (m == NULL) {\n\t\t\tm = history_last_monitor(NULL);\n\t\t}\n\t\tif (m == NULL) {\n\t\t\tm = mon_head;\n\t\t}\n\t}\n\n\tif (m == NULL) {\n\t\treturn false;\n\t}\n\n\tif (d == NULL) {\n\t\td = m->desk;\n\t\tif (d == NULL) {\n\t\t\td = history_last_desktop(m, NULL);\n\t\t}\n\t\tif (d == NULL) {\n\t\t\td = m->desk_head;\n\t\t}\n\t}\n\n\tif (d == NULL) {\n\t\treturn false;\n\t}\n\n\tbool guess = (n == NULL);\n\n\tif (n == NULL && d->root != NULL) {\n\t\tn = d->focus;\n\t\tif (n == NULL) {\n\t\t\tn = history_last_node(d, NULL);\n\t\t}\n\t\tif (n == NULL) {\n\t\t\tn = first_focusable_leaf(d->root);\n\t\t}\n\t}\n\n\tif (n != NULL && !is_focusable(n)) {\n\t\treturn false;\n\t}\n\n\tif ((mon != NULL && mon->desk != d) || n == NULL || n->client == NULL) {\n\t\tclear_input_focus();\n\t}\n\n\tif (m->sticky_count > 0 && m->desk != NULL && d != m->desk) {\n\t\tif (guess && m->desk->focus != NULL && m->desk->focus->sticky) {\n\t\t\tn = m->desk->focus;\n\t\t}\n\n\t\ttransfer_sticky_nodes(m, m->desk, m, d, m->desk->root);\n\n\t\tif (n == NULL && d->focus != NULL) {\n\t\t\tn = d->focus;\n\t\t}\n\t}\n\n\tif (d->focus != NULL && n != d->focus) {\n\t\tneutralize_occluding_windows(m, d, n);\n\t}\n\n\tif (n != NULL && n->client != NULL && n->client->urgent) {\n\t\tset_urgent(m, d, n, false);\n\t}\n\n\tif (mon != m) {\n\t\tif (mon != NULL) {\n\t\t\tfor (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {\n\t\t\t\tdraw_border(e->focus, true, false);\n\t\t\t}\n\t\t}\n\t\tfor (desktop_t *e = m->desk_head; e != NULL; e = e->next) {\n\t\t\tif (e == d) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tdraw_border(e->focus, true, true);\n\t\t}\n\t}\n\n\tif (d->focus != n) {\n\t\tfor (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {\n\t\t\tif (f->client != NULL && !is_descendant(f, n)) {\n\t\t\t\twindow_draw_border(f->id, get_border_color(false, true));\n\t\t\t}\n\t\t}\n\t}\n\n\tdraw_border(n, true, true);\n\n\tbool desk_changed = (m != mon || m->desk != d);\n\tbool has_input_focus = false;\n\n\tif (mon != m) {\n\t\tmon = m;\n\n\t\tif (pointer_follows_monitor) {\n\t\t\tcenter_pointer(m->rectangle);\n\t\t}\n\n\t\tput_status(SBSC_MASK_MONITOR_FOCUS, \"monitor_focus 0x%08X\\n\", m->id);\n\t}\n\n\tif (m->desk != d) {\n\t\tshow_desktop(d);\n\t\tset_input_focus(n);\n\t\thas_input_focus = true;\n\t\thide_desktop(m->desk);\n\t\tm->desk = d;\n\t}\n\n\tif (desk_changed) {\n\t\tewmh_update_current_desktop();\n\t\tput_status(SBSC_MASK_DESKTOP_FOCUS, \"desktop_focus 0x%08X 0x%08X\\n\", m->id, d->id);\n\t}\n\n\td->focus = n;\n\tif (!has_input_focus) {\n\t\tset_input_focus(n);\n\t}\n\tewmh_update_active_window();\n\thistory_add(m, d, n, true);\n\n\tput_status(SBSC_MASK_REPORT);\n\n\tif (n == NULL) {\n\t\tif (focus_follows_pointer) {\n\t\t\tupdate_motion_recorder();\n\t\t}\n\t\treturn true;\n\t}\n\n\tput_status(SBSC_MASK_NODE_FOCUS, \"node_focus 0x%08X 0x%08X 0x%08X\\n\", m->id, d->id, n->id);\n\n\tstack(d, n, true);\n\n\tif (pointer_follows_focus) {\n\t\tcenter_pointer(get_rectangle(m, d, n));\n\t} else if (focus_follows_pointer) {\n\t\tupdate_motion_recorder();\n\t}\n\n\treturn true;\n}\n\nvoid hide_node(desktop_t *d, node_t *n)\n{\n\tif (n == NULL || (!hide_sticky && n->sticky)) {\n\t\treturn;\n\t} else {\n\t\tif (!n->hidden) {\n\t\t\tif (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {\n\t\t\t\twindow_hide(n->presel->feedback);\n\t\t\t}\n\t\t\tif (n->client != NULL) {\n\t\t\t\twindow_hide(n->id);\n\t\t\t}\n\t\t}\n\t\tif (n->client != NULL) {\n\t\t\tn->client->shown = false;\n\t\t}\n\t\thide_node(d, n->first_child);\n\t\thide_node(d, n->second_child);\n\t}\n}\n\nvoid show_node(desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (!n->hidden) {\n\t\t\tif (n->client != NULL) {\n\t\t\t\twindow_show(n->id);\n\t\t\t}\n\t\t\tif (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {\n\t\t\t\twindow_show(n->presel->feedback);\n\t\t\t}\n\t\t}\n\t\tif (n->client != NULL) {\n\t\t\tn->client->shown = true;\n\t\t}\n\t\tshow_node(d, n->first_child);\n\t\tshow_node(d, n->second_child);\n\t}\n}\n\nnode_t *make_node(uint32_t id)\n{\n\tif (id == XCB_NONE) {\n\t\tid = xcb_generate_id(dpy);\n\t}\n\tnode_t *n = calloc(1, sizeof(node_t));\n\tn->id = id;\n\tn->parent = n->first_child = n->second_child = NULL;\n\tn->vacant = n->hidden = n->sticky = n->private = n->locked = n->marked = false;\n\tn->split_ratio = split_ratio;\n\tn->split_type = TYPE_VERTICAL;\n\tn->constraints = (constraints_t) {MIN_WIDTH, MIN_HEIGHT};\n\tn->presel = NULL;\n\tn->client = NULL;\n\treturn n;\n}\n\nclient_t *make_client(void)\n{\n\tclient_t *c = calloc(1, sizeof(client_t));\n\tc->state = c->last_state = STATE_TILED;\n\tc->layer = c->last_layer = LAYER_NORMAL;\n\tsnprintf(c->class_name, sizeof(c->class_name), \"%s\", MISSING_VALUE);\n\tsnprintf(c->instance_name, sizeof(c->instance_name), \"%s\", MISSING_VALUE);\n\tc->border_width = border_width;\n\tc->urgent = false;\n\tc->shown = false;\n\tc->wm_flags = 0;\n\tc->icccm_props.input_hint = true;\n\tc->icccm_props.take_focus = false;\n\tc->icccm_props.delete_window = false;\n\tc->size_hints.flags = 0;\n\tc->honor_size_hints = honor_size_hints;\n\treturn c;\n}\n\nvoid initialize_client(node_t *n)\n{\n\txcb_window_t win = n->id;\n\tclient_t *c = n->client;\n\txcb_icccm_get_wm_protocols_reply_t protos;\n\tif (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protos, NULL) == 1) {\n\t\tfor (uint32_t i = 0; i < protos.atoms_len; i++) {\n\t\t\tif (protos.atoms[i] == WM_TAKE_FOCUS) {\n\t\t\t\tc->icccm_props.take_focus = true;\n\t\t\t} else if (protos.atoms[i] == WM_DELETE_WINDOW) {\n\t\t\t\tc->icccm_props.delete_window = true;\n\t\t\t}\n\t\t}\n\t\txcb_icccm_get_wm_protocols_reply_wipe(&protos);\n\t}\n\txcb_ewmh_get_atoms_reply_t wm_state;\n\tif (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {\n\t\tfor (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {\n#define HANDLE_WM_STATE(s) \\\n\t\t\tif (wm_state.atoms[i] == ewmh->_NET_WM_STATE_##s) { \\\n\t\t\t\tc->wm_flags |= WM_FLAG_##s; continue; \\\n\t\t\t}\n\t\t\tHANDLE_WM_STATE(MODAL)\n\t\t\tHANDLE_WM_STATE(STICKY)\n\t\t\tHANDLE_WM_STATE(MAXIMIZED_VERT)\n\t\t\tHANDLE_WM_STATE(MAXIMIZED_HORZ)\n\t\t\tHANDLE_WM_STATE(SHADED)\n\t\t\tHANDLE_WM_STATE(SKIP_TASKBAR)\n\t\t\tHANDLE_WM_STATE(SKIP_PAGER)\n\t\t\tHANDLE_WM_STATE(HIDDEN)\n\t\t\tHANDLE_WM_STATE(FULLSCREEN)\n\t\t\tHANDLE_WM_STATE(ABOVE)\n\t\t\tHANDLE_WM_STATE(BELOW)\n\t\t\tHANDLE_WM_STATE(DEMANDS_ATTENTION)\n#undef HANDLE_WM_STATE\n\t\t}\n\t\txcb_ewmh_get_atoms_reply_wipe(&wm_state);\n\t}\n\txcb_icccm_wm_hints_t hints;\n\tif (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1\n\t\t&& (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {\n\t\tc->icccm_props.input_hint = hints.input;\n\t}\n\txcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &c->size_hints, NULL);\n}\n\nbool is_focusable(node_t *n)\n{\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client != NULL && !f->hidden) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool is_leaf(node_t *n)\n{\n\treturn (n != NULL && n->first_child == NULL && n->second_child == NULL);\n}\n\nbool is_first_child(node_t *n)\n{\n\treturn (n != NULL && n->parent != NULL && n->parent->first_child == n);\n}\n\nbool is_second_child(node_t *n)\n{\n\treturn (n != NULL && n->parent != NULL && n->parent->second_child == n);\n}\n\nunsigned int clients_count_in(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn 0;\n\t} else {\n\t\treturn (n->client != NULL ? 1 : 0) +\n\t\t        clients_count_in(n->first_child) +\n\t\t        clients_count_in(n->second_child);\n\t}\n}\n\nnode_t *brother_tree(node_t *n)\n{\n\tif (n == NULL || n->parent == NULL) {\n\t\treturn NULL;\n\t}\n\tif (is_first_child(n)) {\n\t\treturn n->parent->second_child;\n\t} else {\n\t\treturn n->parent->first_child;\n\t}\n}\n\nnode_t *first_extrema(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t} else if (n->first_child == NULL) {\n\t\treturn n;\n\t} else {\n\t\treturn first_extrema(n->first_child);\n\t}\n}\n\nnode_t *second_extrema(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t} else if (n->second_child == NULL) {\n\t\treturn n;\n\t} else {\n\t\treturn second_extrema(n->second_child);\n\t}\n}\n\nnode_t *first_focusable_leaf(node_t *n)\n{\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client != NULL && !f->hidden) {\n\t\t\treturn f;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nnode_t *next_node(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (n->second_child != NULL) {\n\t\treturn first_extrema(n->second_child);\n\t} else {\n\t\tnode_t *p = n;\n\t\twhile (is_second_child(p)) {\n\t\t\tp = p->parent;\n\t\t}\n\t\tif (is_first_child(p)) {\n\t\t\treturn p->parent;\n\t\t} else {\n\t\t\treturn NULL;\n\t\t}\n\t}\n}\n\nnode_t *prev_node(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (n->first_child != NULL) {\n\t\treturn second_extrema(n->first_child);\n\t} else {\n\t\tnode_t *p = n;\n\t\twhile (is_first_child(p)) {\n\t\t\tp = p->parent;\n\t\t}\n\t\tif (is_second_child(p)) {\n\t\t\treturn p->parent;\n\t\t} else {\n\t\t\treturn NULL;\n\t\t}\n\t}\n}\n\nnode_t *next_leaf(node_t *n, node_t *r)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t}\n\tnode_t *p = n;\n\twhile (is_second_child(p) && p != r) {\n\t\tp = p->parent;\n\t}\n\tif (p == r) {\n\t\treturn NULL;\n\t}\n\treturn first_extrema(p->parent->second_child);\n}\n\nnode_t *prev_leaf(node_t *n, node_t *r)\n{\n\tif (n == NULL) {\n\t\treturn NULL;\n\t}\n\tnode_t *p = n;\n\twhile (is_first_child(p) && p != r) {\n\t\tp = p->parent;\n\t}\n\tif (p == r) {\n\t\treturn NULL;\n\t}\n\treturn second_extrema(p->parent->first_child);\n}\n\nnode_t *next_tiled_leaf(node_t *n, node_t *r)\n{\n\tnode_t *next = next_leaf(n, r);\n\tif (next == NULL || (next->client != NULL && !next->vacant)) {\n\t\treturn next;\n\t} else {\n\t\treturn next_tiled_leaf(next, r);\n\t}\n}\n\nnode_t *prev_tiled_leaf(node_t *n, node_t *r)\n{\n\tnode_t *prev = prev_leaf(n, r);\n\tif (prev == NULL || (prev->client != NULL && !prev->vacant)) {\n\t\treturn prev;\n\t} else {\n\t\treturn prev_tiled_leaf(prev, r);\n\t}\n}\n\n/* Returns true if *b* is adjacent to *a* in the direction *dir* */\nbool is_adjacent(node_t *a, node_t *b, direction_t dir)\n{\n\tswitch (dir) {\n\t\tcase DIR_EAST:\n\t\t\treturn (a->rectangle.x + a->rectangle.width) == b->rectangle.x;\n\t\t\tbreak;\n\t\tcase DIR_SOUTH:\n\t\t\treturn (a->rectangle.y + a->rectangle.height) == b->rectangle.y;\n\t\t\tbreak;\n\t\tcase DIR_WEST:\n\t\t\treturn (b->rectangle.x + b->rectangle.width) == a->rectangle.x;\n\t\t\tbreak;\n\t\tcase DIR_NORTH:\n\t\t\treturn (b->rectangle.y + b->rectangle.height) == a->rectangle.y;\n\t\t\tbreak;\n\t}\n\treturn false;\n}\n\nnode_t *find_fence(node_t *n, direction_t dir)\n{\n\tnode_t *p;\n\n\tif (n == NULL) {\n\t\treturn NULL;\n\t}\n\n\tp = n->parent;\n\n\twhile (p != NULL) {\n\t\tif ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||\n\t\t    (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||\n\t\t    (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||\n\t\t    (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))\n\t\t\treturn p;\n\t\tp = p->parent;\n\t}\n\n\treturn NULL;\n}\n\n/* returns *true* if *a* is a child of *b* */\nbool is_child(node_t *a, node_t *b)\n{\n\tif (a == NULL || b == NULL) {\n\t\treturn false;\n\t}\n\treturn (a->parent != NULL && a->parent == b);\n}\n\n/* returns *true* if *a* is a descendant of *b* */\nbool is_descendant(node_t *a, node_t *b)\n{\n\tif (a == NULL || b == NULL) {\n\t\treturn false;\n\t}\n\twhile (a != b && a != NULL) {\n\t\ta = a->parent;\n\t}\n\treturn a == b;\n}\n\nbool find_by_id(uint32_t id, coordinates_t *loc)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tnode_t *n = find_by_id_in(d->root, id);\n\t\t\tif (n != NULL) {\n\t\t\t\tloc->monitor = m;\n\t\t\t\tloc->desktop = d;\n\t\t\t\tloc->node = n;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nnode_t *find_by_id_in(node_t *r, uint32_t id)\n{\n\tif (r == NULL) {\n\t\treturn NULL;\n\t} else if (r->id == id) {\n\t\treturn r;\n\t} else {\n\t\tnode_t *f = find_by_id_in(r->first_child, id);\n\t\tif (f != NULL) {\n\t\t\treturn f;\n\t\t} else {\n\t\t\treturn find_by_id_in(r->second_child, id);\n\t\t}\n\t}\n}\n\nvoid find_any_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tif (find_any_node_in(m, d, d->root, ref, dst, sel)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool find_any_node_in(monitor_t *m, desktop_t *d, node_t *n, coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tif (n == NULL) {\n\t\treturn false;\n\t} else {\n\t\tcoordinates_t loc = {m, d, n};\n\t\tif (node_matches(&loc, ref, sel)) {\n\t\t\t*dst = loc;\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (find_any_node_in(m, d, n->first_child, ref, dst, sel)) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn find_any_node_in(m, d, n->second_child, ref, dst, sel);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid find_first_ancestor(coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tif (ref->node == NULL) {\n\t\treturn;\n\t}\n\n\tcoordinates_t loc = {ref->monitor, ref->desktop, ref->node};\n\twhile ((loc.node = loc.node->parent) != NULL) {\n\t\tif (node_matches(&loc, ref, sel)) {\n\t\t\t*dst = loc;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/* Based on https://github.com/ntrrgc/right-window */\nvoid find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t *sel)\n{\n\txcb_rectangle_t rect = get_rectangle(ref->monitor, ref->desktop, ref->node);\n\tuint32_t md = UINT32_MAX, mr = UINT32_MAX;\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tdesktop_t *d = m->desk;\n\t\tfor (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {\n\t\t\tcoordinates_t loc = {m, d, f};\n\t\t\txcb_rectangle_t r = get_rectangle(m, d, f);\n\t\t\tif (f == ref->node ||\n\t\t\t    f->client == NULL ||\n\t\t\t    f->hidden ||\n\t\t\t    is_descendant(f, ref->node) ||\n\t\t\t    !node_matches(&loc, ref, sel) ||\n\t\t\t    !on_dir_side(rect, r, dir)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tuint32_t fd = boundary_distance(rect, r, dir);\n\t\t\tuint32_t fr = history_rank(f);\n\t\t\tif (fd < md || (fd == md && fr < mr)) {\n\t\t\t\tmd = fd;\n\t\t\t\tmr = fr;\n\t\t\t\t*dst = loc;\n\t\t\t}\n\t\t}\n\t}\n}\n\nunsigned int node_area(desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn 0;\n\t}\n\treturn area(get_rectangle(NULL, d, n));\n}\n\nint tiled_count(node_t *n, bool include_receptacles)\n{\n\tif (n == NULL) {\n\t\treturn 0;\n\t}\n\tint cnt = 0;\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (!f->hidden && ((include_receptacles && f->client == NULL) ||\n\t\t                   (f->client != NULL && IS_TILED(f->client)))) {\n\t\t\tcnt++;\n\t\t}\n\t}\n\treturn cnt;\n}\n\nvoid find_by_area(area_peak_t ap, coordinates_t *ref, coordinates_t *dst, node_select_t *sel)\n{\n\tunsigned int p_area;\n\tif (ap == AREA_BIGGEST) {\n\t\tp_area = 0;\n\t} else {\n\t\tp_area = UINT_MAX;\n\t}\n\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tfor (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {\n\t\t\t\tcoordinates_t loc = {m, d, f};\n\t\t\t\tif (f->vacant || !node_matches(&loc, ref, sel)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tunsigned int f_area = node_area(d, f);\n\t\t\t\tif ((ap == AREA_BIGGEST && f_area > p_area) || (ap == AREA_SMALLEST && f_area < p_area)) {\n\t\t\t\t\t*dst = loc;\n\t\t\t\t\tp_area = f_area;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid rotate_tree(node_t *n, int deg)\n{\n\trotate_tree_rec(n, deg);\n\trebuild_constraints_from_leaves(n);\n\trebuild_constraints_towards_root(n);\n}\n\nvoid rotate_tree_rec(node_t *n, int deg)\n{\n\tif (n == NULL || is_leaf(n) || deg == 0) {\n\t\treturn;\n\t}\n\n\tnode_t *tmp;\n\n\tif ((deg == 90 && n->split_type == TYPE_HORIZONTAL) ||\n\t    (deg == 270 && n->split_type == TYPE_VERTICAL) ||\n\t    deg == 180) {\n\t\ttmp = n->first_child;\n\t\tn->first_child = n->second_child;\n\t\tn->second_child = tmp;\n\t\tn->split_ratio = 1.0 - n->split_ratio;\n\t}\n\n\tif (deg != 180) {\n\t\tif (n->split_type == TYPE_HORIZONTAL) {\n\t\t\tn->split_type = TYPE_VERTICAL;\n\t\t} else if (n->split_type == TYPE_VERTICAL) {\n\t\t\tn->split_type = TYPE_HORIZONTAL;\n\t\t}\n\t}\n\n\trotate_tree_rec(n->first_child, deg);\n\trotate_tree_rec(n->second_child, deg);\n}\n\nvoid flip_tree(node_t *n, flip_t flp)\n{\n\tif (n == NULL || is_leaf(n)) {\n\t\treturn;\n\t}\n\n\tnode_t *tmp;\n\n\tif ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) ||\n\t    (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {\n\t\ttmp = n->first_child;\n\t\tn->first_child = n->second_child;\n\t\tn->second_child = tmp;\n\t\tn->split_ratio = 1.0 - n->split_ratio;\n\t}\n\n\tflip_tree(n->first_child, flp);\n\tflip_tree(n->second_child, flp);\n}\n\nvoid equalize_tree(node_t *n)\n{\n\tif (n == NULL || n->vacant) {\n\t\treturn;\n\t} else {\n\t\tn->split_ratio = split_ratio;\n\t\tequalize_tree(n->first_child);\n\t\tequalize_tree(n->second_child);\n\t}\n}\n\nint balance_tree(node_t *n)\n{\n\tif (n == NULL || n->vacant) {\n\t\treturn 0;\n\t} else if (is_leaf(n)) {\n\t\treturn 1;\n\t} else {\n\t\tint b1 = balance_tree(n->first_child);\n\t\tint b2 = balance_tree(n->second_child);\n\t\tint b = b1 + b2;\n\t\tif (b1 > 0 && b2 > 0) {\n\t\t\tn->split_ratio = (double) b1 / b;\n\t\t}\n\t\treturn b;\n\t}\n}\n\n/* Adjust the split ratios so that they keep their position\n * despite the potential alteration of their rectangle. */\nvoid adjust_ratios(node_t *n, xcb_rectangle_t rect)\n{\n#define NULL_OR_VACANT(n) ((n) == NULL || (n)->vacant)\n\tif (NULL_OR_VACANT(n)) {\n\t\treturn;\n\t}\n\n\tdouble ratio;\n\n\tif (n->split_type == TYPE_VERTICAL) {\n\t\tdouble position = (double) n->rectangle.x + n->split_ratio * (double) n->rectangle.width;\n\t\tratio = (position - (double) rect.x) / (double) rect.width;\n\t} else {\n\t\tdouble position = (double) n->rectangle.y + n->split_ratio * (double) n->rectangle.height;\n\t\tratio = (position - (double) rect.y) / (double) rect.height;\n\t}\n\n\tratio = MAX(0.0, ratio);\n\tratio = MIN(1.0, ratio);\n\tn->split_ratio = ratio;\n\n\txcb_rectangle_t first_rect;\n\txcb_rectangle_t second_rect;\n\tunsigned int fence;\n\n\tif (NULL_OR_VACANT(n->first_child)) {\n\t\tadjust_ratios(n->second_child, rect);\n\t\treturn;\n\t}\n\tif (NULL_OR_VACANT(n->second_child)) {\n\t\tadjust_ratios(n->first_child, rect);\n\t\treturn;\n\t}\n#undef NULL_OR_VACANT\n\n\tif (n->split_type == TYPE_VERTICAL) {\n\t\tfence = rect.width * n->split_ratio;\n\t\tfirst_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};\n\t\tsecond_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};\n\t} else {\n\t\tfence = rect.height * n->split_ratio;\n\t\tfirst_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};\n\t\tsecond_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};\n\t}\n\n\tadjust_ratios(n->first_child, first_rect);\n\tadjust_ratios(n->second_child, second_rect);\n}\n\nvoid unlink_node(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (d == NULL || n == NULL) {\n\t\treturn;\n\t}\n\n\tnode_t *p = n->parent;\n\n\tif (p == NULL) {\n\t\td->root = NULL;\n\t\td->focus = NULL;\n\t\tput_status(SBSC_MASK_REPORT);\n\t} else {\n\t\tif (d->focus == p || is_descendant(d->focus, n)) {\n\t\t\td->focus = NULL;\n\t\t}\n\n\t\thistory_remove(d, p, false);\n\t\tcancel_presel(m, d, p);\n\n\t\tif (p->sticky) {\n\t\t\tm->sticky_count--;\n\t\t}\n\n\t\tnode_t *b = brother_tree(n);\n\t\tnode_t *g = p->parent;\n\n\t\tb->parent = g;\n\n\t\tif (g != NULL) {\n\t\t\tif (is_first_child(p)) {\n\t\t\t\tg->first_child = b;\n\t\t\t} else {\n\t\t\t\tg->second_child = b;\n\t\t\t}\n\t\t} else {\n\t\t\td->root = b;\n\t\t}\n\n\t\tif (!n->vacant && removal_adjustment) {\n\t\t\tif (automatic_scheme == SCHEME_SPIRAL) {\n\t\t\t\tif (is_first_child(n)) {\n\t\t\t\t\trotate_tree(b, 270);\n\t\t\t\t} else {\n\t\t\t\t\trotate_tree(b, 90);\n\t\t\t\t}\n\t\t\t} else if (automatic_scheme == SCHEME_LONGEST_SIDE || g == NULL) {\n\t\t\t\tif (p != NULL) {\n\t\t\t\t\tif (p->rectangle.width > p->rectangle.height) {\n\t\t\t\t\t\tb->split_type = TYPE_VERTICAL;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb->split_type = TYPE_HORIZONTAL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (automatic_scheme == SCHEME_ALTERNATE) {\n\t\t\t\tif (g->split_type == TYPE_HORIZONTAL) {\n\t\t\t\t\tb->split_type = TYPE_VERTICAL;\n\t\t\t\t} else {\n\t\t\t\t\tb->split_type = TYPE_HORIZONTAL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfree(p);\n\t\tn->parent = NULL;\n\n\t\tpropagate_flags_upward(m, d, b);\n\t}\n}\n\nvoid close_node(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else if (n->client != NULL) {\n\t\tif (n->client->icccm_props.delete_window) {\n\t\t\tsend_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);\n\t\t} else {\n\t\t\txcb_kill_client(dpy, n->id);\n\t\t}\n\t} else {\n\t\tclose_node(n->first_child);\n\t\tclose_node(n->second_child);\n\t}\n}\n\nvoid kill_node(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tif (IS_RECEPTACLE(n)) {\n\t\tput_status(SBSC_MASK_NODE_REMOVE, \"node_remove 0x%08X 0x%08X 0x%08X\\n\", m->id, d->id, n->id);\n\t\tremove_node(m, d, n);\n\t} else {\n\t\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\t\tif (f->client != NULL) {\n\t\t\t\txcb_kill_client(dpy, f->id);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid remove_node(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tunlink_node(m, d, n);\n\thistory_remove(d, n, true);\n\tremove_stack_node(n);\n\tcancel_presel_in(m, d, n);\n\tif (m->sticky_count > 0 && d == m->desk) {\n\t\tm->sticky_count -= sticky_count(n);\n\t}\n\tclients_count -= clients_count_in(n);\n\tif (is_descendant(grabbed_node, n)) {\n\t\tgrabbed_node = NULL;\n\t}\n\tfree_node(n);\n\n\tif (single_monocle && d->layout != LAYOUT_MONOCLE && tiled_count(d->root, true) <= 1) {\n\t\tset_layout(m, d, LAYOUT_MONOCLE, false);\n\t}\n\n\tewmh_update_client_list(false);\n\tewmh_update_client_list(true);\n\n\tif (mon != NULL && d->focus == NULL) {\n\t\tif (d == mon->desk) {\n\t\t\tfocus_node(m, d, NULL);\n\t\t} else {\n\t\t\tactivate_node(m, d, NULL);\n\t\t}\n\t}\n}\n\nvoid free_node(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\tnode_t *first_child = n->first_child;\n\tnode_t *second_child = n->second_child;\n\tfree(n->client);\n\tfree(n);\n\tfree_node(first_child);\n\tfree_node(second_child);\n}\n\nbool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2, bool follow)\n{\n\tif (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||\n\t    (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||\n\t                  (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {\n\t\treturn false;\n\t}\n\n\tput_status(SBSC_MASK_NODE_SWAP, \"node_swap 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\\n\", m1->id, d1->id, n1->id, m2->id, d2->id, n2->id);\n\n\tnode_t *pn1 = n1->parent;\n\tnode_t *pn2 = n2->parent;\n\tbool n1_first_child = is_first_child(n1);\n\tbool n2_first_child = is_first_child(n2);\n\tbool n1_held_focus = is_descendant(d1->focus, n1);\n\tbool n2_held_focus = is_descendant(d2->focus, n2);\n\tnode_t *last_d1_focus = d1->focus;\n\tnode_t *last_d2_focus = d2->focus;\n\n\tif (pn1 != NULL) {\n\t\tif (n1_first_child) {\n\t\t\tpn1->first_child = n2;\n\t\t} else {\n\t\t\tpn1->second_child = n2;\n\t\t}\n\t}\n\n\tif (pn2 != NULL) {\n\t\tif (n2_first_child) {\n\t\t\tpn2->first_child = n1;\n\t\t} else {\n\t\t\tpn2->second_child = n1;\n\t\t}\n\t}\n\n\tn1->parent = pn2;\n\tn2->parent = pn1;\n\n\tpropagate_flags_upward(m2, d2, n1);\n\tpropagate_flags_upward(m1, d1, n2);\n\n\tif (d1 != d2) {\n\t\tif (d1->root == n1) {\n\t\t\td1->root = n2;\n\t\t}\n\n\t\tif (d2->root == n2) {\n\t\t\td2->root = n1;\n\t\t}\n\n\t\tif (n1_held_focus) {\n\t\t\td1->focus = n2_held_focus ? last_d2_focus : n2;\n\t\t}\n\n\t\tif (n2_held_focus) {\n\t\t\td2->focus = n1_held_focus ? last_d1_focus : n1;\n\t\t}\n\n\t\tif (m1 != m2) {\n\t\t\tadapt_geometry(&m2->rectangle, &m1->rectangle, n2);\n\t\t\tadapt_geometry(&m1->rectangle, &m2->rectangle, n1);\n\t\t}\n\n\t\tewmh_set_wm_desktop(n1, d2);\n\t\tewmh_set_wm_desktop(n2, d1);\n\n\t\thistory_remove(d1, n1, true);\n\t\thistory_remove(d2, n2, true);\n\n\t\tbool d1_was_focused = (d1 == mon->desk);\n\t\tbool d2_was_focused = (d2 == mon->desk);\n\n\t\tif (m1->desk != d1 && m2->desk == d2) {\n\t\t\tshow_node(d2, n1);\n\t\t\tif (!follow || !d2_was_focused || !n2_held_focus) {\n\t\t\t\thide_node(d2, n2);\n\t\t\t}\n\t\t} else if (m1->desk == d1 && m2->desk != d2) {\n\t\t\tif (!follow || !d1_was_focused || !n1_held_focus) {\n\t\t\t\thide_node(d1, n1);\n\t\t\t}\n\t\t\tshow_node(d1, n2);\n\t\t}\n\n\t\tif (single_monocle) {\n\t\t\tlayout_t l1 = tiled_count(d1->root, true) <= 1 ? LAYOUT_MONOCLE : d1->user_layout;\n\t\t\tlayout_t l2 = tiled_count(d2->root, true) <= 1 ? LAYOUT_MONOCLE : d2->user_layout;\n\t\t\tset_layout(m1, d1, l1, false);\n\t\t\tset_layout(m2, d2, l2, false);\n\t\t}\n\n\t\tif (n1_held_focus) {\n\t\t\tif (d1_was_focused) {\n\t\t\t\tif (follow) {\n\t\t\t\t\tfocus_node(m2, d2, last_d1_focus);\n\t\t\t\t} else {\n\t\t\t\t\tfocus_node(m1, d1, d1->focus);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tactivate_node(m1, d1, d1->focus);\n\t\t\t}\n\t\t} else {\n\t\t\tdraw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));\n\t\t}\n\n\t\tif (n2_held_focus) {\n\t\t\tif (d2_was_focused) {\n\t\t\t\tif (follow) {\n\t\t\t\t\tfocus_node(m1, d1, last_d2_focus);\n\t\t\t\t} else {\n\t\t\t\t\tfocus_node(m2, d2, d2->focus);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tactivate_node(m2, d2, d2->focus);\n\t\t\t}\n\t\t} else {\n\t\t\tdraw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));\n\t\t}\n\t} else {\n\t\tif (!n1_held_focus) {\n\t\t\tdraw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));\n\t\t}\n\t\tif (!n2_held_focus) {\n\t\t\tdraw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));\n\t\t}\n\t}\n\n\tarrange(m1, d1);\n\n\tif (d1 != d2) {\n\t\tarrange(m2, d2);\n\t} else {\n\t\tif (pointer_follows_focus && (n1_held_focus || n2_held_focus)) {\n\t\t\tcenter_pointer(get_rectangle(m1, d1, d1->focus));\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd, bool follow)\n{\n\tif (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {\n\t\treturn false;\n\t}\n\n\tunsigned int sc = (ms->sticky_count > 0 && ds == ms->desk) ? sticky_count(ns) : 0;\n\tif (sticky_still && sc > 0 && dd != md->desk) {\n\t\treturn false;\n\t}\n\n\tput_status(SBSC_MASK_NODE_TRANSFER, \"node_transfer 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\\n\", ms->id, ds->id, ns->id, md->id, dd->id, nd!=NULL?nd->id:0);\n\n\tbool held_focus = is_descendant(ds->focus, ns);\n\t/* avoid ending up with a dangling pointer (because of unlink_node) */\n\tnode_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;\n\tbool ds_was_focused = (ds == mon->desk);\n\n\tif (held_focus && ds_was_focused) {\n\t\tclear_input_focus();\n\t}\n\n\tunlink_node(ms, ds, ns);\n\tinsert_node(md, dd, ns, nd);\n\n\tif (md != ms) {\n\t\tif (ns->client == NULL || monitor_from_client(ns->client) != md) {\n\t\t\tadapt_geometry(&ms->rectangle, &md->rectangle, ns);\n\t\t}\n\t\tms->sticky_count -= sc;\n\t\tmd->sticky_count += sc;\n\t}\n\n\tif (ds != dd) {\n\t\tewmh_set_wm_desktop(ns, dd);\n\t\tif (sticky_still) {\n\t\t\tif (ds == ms->desk && dd != md->desk) {\n\t\t\t\thide_node(ds, ns);\n\t\t\t} else if (ds != ms->desk && dd == md->desk) {\n\t\t\t\tshow_node(dd, ns);\n\t\t\t}\n\t\t}\n\t}\n\n\thistory_remove(ds, ns, true);\n\tstack(dd, ns, false);\n\n\tif (ds == dd) {\n\t\tif (held_focus) {\n\t\t\tif (ds_was_focused) {\n\t\t\t\tfocus_node(ms, ds, last_ds_focus);\n\t\t\t} else {\n\t\t\t\tactivate_node(ms, ds, last_ds_focus);\n\t\t\t}\n\t\t} else {\n\t\t\tdraw_border(ns, is_descendant(ns, ds->focus), (ms == mon));\n\t\t}\n\t} else {\n\t\tif (single_monocle) {\n\t\t\tif (ds->layout != LAYOUT_MONOCLE && tiled_count(ds->root, true) <= 1) {\n\t\t\t\tset_layout(ms, ds, LAYOUT_MONOCLE, false);\n\t\t\t}\n\t\t\tif (dd->layout == LAYOUT_MONOCLE && tiled_count(dd->root, true) > 1) {\n\t\t\t\tset_layout(md, dd, dd->user_layout, false);\n\t\t\t}\n\t\t}\n\t\tif (held_focus) {\n\t\t\tif (follow) {\n\t\t\t\tif (ds_was_focused) {\n\t\t\t\t\tfocus_node(md, dd, last_ds_focus);\n\t\t\t\t}\n\t\t\t\tactivate_node(ms, ds, ds->focus);\n\t\t\t} else {\n\t\t\t\tif (ds_was_focused) {\n\t\t\t\t\tfocus_node(ms, ds, ds->focus);\n\t\t\t\t} else {\n\t\t\t\t\tactivate_node(ms, ds, ds->focus);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!held_focus || !follow || !ds_was_focused) {\n\t\t\tif (dd->focus == ns) {\n\t\t\t\tif (dd == mon->desk) {\n\t\t\t\t\tfocus_node(md, dd, held_focus ? last_ds_focus : ns);\n\t\t\t\t} else {\n\t\t\t\t\tactivate_node(md, dd, held_focus ? last_ds_focus : ns);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdraw_border(ns, is_descendant(ns, dd->focus), (md == mon));\n\t\t\t}\n\t\t}\n\t}\n\n\tarrange(ms, ds);\n\n\tif (ds != dd) {\n\t\tarrange(md, dd);\n\t}\n\n\treturn true;\n}\n\nbool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t *sel)\n{\n\tmonitor_t *m = ref->monitor;\n\tdesktop_t *d = ref->desktop;\n\tnode_t *n = ref->node;\n\tn = (dir == CYCLE_PREV ? prev_node(n) : next_node(n));\n\n#define HANDLE_BOUNDARIES(m, d, n)  \\\n\twhile (n == NULL) { \\\n\t\td = (dir == CYCLE_PREV ? d->prev : d->next); \\\n\t\tif (d == NULL) { \\\n\t\t\tm = (dir == CYCLE_PREV ? m->prev : m->next); \\\n\t\t\tif (m == NULL) { \\\n\t\t\t\tm = (dir == CYCLE_PREV ? mon_tail : mon_head); \\\n\t\t\t} \\\n\t\t\td = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \\\n\t\t} \\\n\t\tn = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); \\\n\t\tif (ref->node == NULL && d == ref->desktop) { \\\n\t\t\tbreak; \\\n\t\t} \\\n\t}\n\tHANDLE_BOUNDARIES(m, d, n);\n\n\twhile (n != ref->node) {\n\t\tcoordinates_t loc = {m, d, n};\n\t\tif (node_matches(&loc, ref, sel)) {\n\t\t\t*dst = loc;\n\t\t\treturn true;\n\t\t}\n\t\tn = (dir == CYCLE_PREV ? prev_node(n) : next_node(n));\n\t\tHANDLE_BOUNDARIES(m, d, n);\n\t\tif (ref->node == NULL && d == ref->desktop) {\n\t\t\tbreak;\n\t\t}\n\t}\n#undef HANDLE_BOUNDARIES\n\treturn false;\n}\n\nvoid circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)\n{\n\tif (tiled_count(n, false) < 2) {\n\t\treturn;\n\t}\n\tnode_t *p = d->focus->parent;\n\tbool focus_first_child = is_first_child(d->focus);\n\tif (dir == CIRCULATE_FORWARD) {\n\t\tnode_t *e = second_extrema(n);\n\t\twhile (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {\n\t\t\te = prev_leaf(e, n);\n\t\t}\n\t\tfor (node_t *s = e, *f = prev_tiled_leaf(s, n); f != NULL; s = prev_tiled_leaf(f, n), f = prev_tiled_leaf(s, n)) {\n\t\t\tswap_nodes(m, d, f, m, d, s, false);\n\t\t}\n\t} else {\n\t\tnode_t *e = first_extrema(n);\n\t\twhile (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {\n\t\t\te = next_leaf(e, n);\n\t\t}\n\t\tfor (node_t *f = e, *s = next_tiled_leaf(f, n); s != NULL; f = next_tiled_leaf(s, n), s = next_tiled_leaf(f, n)) {\n\t\t\tswap_nodes(m, d, f, m, d, s, false);\n\t\t}\n\t}\n\tif (p != NULL) {\n\t\tnode_t *f = focus_first_child ? p->first_child : p->second_child;\n\t\tif (is_leaf(f)) {\n\t\t\tif (d == mon->desk) {\n\t\t\t\tfocus_node(m, d, f);\n\t\t\t} else {\n\t\t\t\tactivate_node(m, d, f);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n->vacant == value) {\n\t\treturn;\n\t}\n\n\tpropagate_vacant_downward(m, d, n, value);\n\tpropagate_vacant_upward(m, d, n);\n}\n\nvoid set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n->vacant == value) {\n\t\treturn;\n\t}\n\n\tn->vacant = value;\n\n\tif (value) {\n\t\tcancel_presel(m, d, n);\n\t}\n}\n\nvoid propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tset_vacant_local(m, d, n, value);\n\n\tpropagate_vacant_downward(m, d, n->first_child, value);\n\tpropagate_vacant_downward(m, d, n->second_child, value);\n}\n\nvoid propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tnode_t *p = n->parent;\n\n\tif (p != NULL) {\n\t\tset_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));\n\t}\n\n\tpropagate_vacant_upward(m, d, p);\n}\n\nbool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)\n{\n\tif (n == NULL || n->client == NULL || n->client->layer == l) {\n\t\treturn false;\n\t}\n\n\tn->client->last_layer = n->client->layer;\n\tn->client->layer = l;\n\n\tif (l == LAYER_ABOVE) {\n\t\tn->client->wm_flags |= WM_FLAG_ABOVE;\n\t\tn->client->wm_flags &= ~WM_FLAG_BELOW;\n\t} else if (l == LAYER_BELOW) {\n\t\tn->client->wm_flags |= WM_FLAG_BELOW;\n\t\tn->client->wm_flags &= ~WM_FLAG_ABOVE;\n\t} else {\n\t\tn->client->wm_flags &= ~(WM_FLAG_ABOVE | WM_FLAG_BELOW);\n\t}\n\n\tewmh_wm_state_update(n);\n\n\tput_status(SBSC_MASK_NODE_LAYER, \"node_layer 0x%08X 0x%08X 0x%08X %s\\n\", m->id, d->id, n->id, LAYER_STR(l));\n\n\tif (d->focus == n) {\n\t\tneutralize_occluding_windows(m, d, n);\n\t}\n\n\tstack(d, n, (d->focus == n));\n\n\treturn true;\n}\n\nbool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)\n{\n\tif (n == NULL || n->client == NULL || n->client->state == s) {\n\t\treturn false;\n\t}\n\n\tclient_t *c = n->client;\n\n\tbool was_tiled = IS_TILED(c);\n\n\tc->last_state = c->state;\n\tc->state = s;\n\n\tswitch (c->last_state) {\n\t\tcase STATE_TILED:\n\t\tcase STATE_PSEUDO_TILED:\n\t\t\tbreak;\n\t\tcase STATE_FLOATING:\n\t\t\tset_floating(m, d, n, false);\n\t\t\tbreak;\n\t\tcase STATE_FULLSCREEN:\n\t\t\tset_fullscreen(m, d, n, false);\n\t\t\tbreak;\n\t}\n\n\tput_status(SBSC_MASK_NODE_STATE, \"node_state 0x%08X 0x%08X 0x%08X %s off\\n\", m->id, d->id, n->id, STATE_STR(c->last_state));\n\n\tswitch (c->state) {\n\t\tcase STATE_TILED:\n\t\tcase STATE_PSEUDO_TILED:\n\t\t\tbreak;\n\t\tcase STATE_FLOATING:\n\t\t\tset_floating(m, d, n, true);\n\t\t\tbreak;\n\t\tcase STATE_FULLSCREEN:\n\t\t\tset_fullscreen(m, d, n, true);\n\t\t\tbreak;\n\t}\n\n\tput_status(SBSC_MASK_NODE_STATE, \"node_state 0x%08X 0x%08X 0x%08X %s on\\n\", m->id, d->id, n->id, STATE_STR(c->state));\n\n\tif (n == m->desk->focus) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n\n\tif (single_monocle && was_tiled != IS_TILED(c)) {\n\t\tif (was_tiled && d->layout != LAYOUT_MONOCLE && tiled_count(d->root, true) <= 1) {\n\t\t\tset_layout(m, d, LAYOUT_MONOCLE, false);\n\t\t} else if (!was_tiled && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) {\n\t\t\tset_layout(m, d, d->user_layout, false);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tcancel_presel(m, d, n);\n\tif (!n->hidden) {\n\t\tset_vacant(m, d, n, value);\n\t}\n\n\tif (!value && d->focus == n) {\n\t\tneutralize_occluding_windows(m, d, n);\n\t}\n\n\tstack(d, n, (d->focus == n));\n}\n\nvoid set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tclient_t *c = n->client;\n\n\tcancel_presel(m, d, n);\n\tif (!n->hidden) {\n\t\tset_vacant(m, d, n, value);\n\t}\n\n\tif (value) {\n\t\tc->wm_flags |= WM_FLAG_FULLSCREEN;\n\t} else {\n\t\tc->wm_flags &= ~WM_FLAG_FULLSCREEN;\n\t\tif (d->focus == n) {\n\t\t\tneutralize_occluding_windows(m, d, n);\n\t\t}\n\t}\n\n\tewmh_wm_state_update(n);\n\tstack(d, n, (d->focus == n));\n}\n\nvoid neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tbool changed = false;\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tfor (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {\n\t\t\tif (a != f && a->client != NULL && f->client != NULL &&\n\t\t\t    IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {\n\t\t\t\tset_state(m, d, a, a->client->last_state);\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t}\n\tif (changed) {\n\t\tarrange(m, d);\n\t}\n}\n\nvoid rebuild_constraints_from_leaves(node_t *n)\n{\n\tif (n == NULL || is_leaf(n)) {\n\t\treturn;\n\t} else {\n\t\trebuild_constraints_from_leaves(n->first_child);\n\t\trebuild_constraints_from_leaves(n->second_child);\n\t\tupdate_constraints(n);\n\t}\n}\n\nvoid rebuild_constraints_towards_root(node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tnode_t *p = n->parent;\n\n\tif (p != NULL) {\n\t\tupdate_constraints(p);\n\t}\n\n\trebuild_constraints_towards_root(p);\n}\n\nvoid update_constraints(node_t *n)\n{\n\tif (n == NULL || is_leaf(n)) {\n\t\treturn;\n\t}\n\tif (n->split_type == TYPE_VERTICAL) {\n\t\tn->constraints.min_width = n->first_child->constraints.min_width + n->second_child->constraints.min_width;\n\t\tn->constraints.min_height = MAX(n->first_child->constraints.min_height, n->second_child->constraints.min_height);\n\t} else {\n\t\tn->constraints.min_width = MAX(n->first_child->constraints.min_width, n->second_child->constraints.min_width);\n\t\tn->constraints.min_height = n->first_child->constraints.min_height + n->second_child->constraints.min_height;\n\t}\n}\n\nvoid propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tnode_t *p = n->parent;\n\n\tif (p != NULL) {\n\t\tset_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));\n\t\tset_hidden_local(m, d, p, (p->first_child->hidden && p->second_child->hidden));\n\t\tupdate_constraints(p);\n\t}\n\n\tpropagate_flags_upward(m, d, p);\n}\n\nvoid set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL || n->hidden == value) {\n\t\treturn;\n\t}\n\n\tbool held_focus = is_descendant(d->focus, n);\n\n\tpropagate_hidden_downward(m, d, n, value);\n\tpropagate_hidden_upward(m, d, n);\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X hidden %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\n\tif (held_focus || d->focus == NULL) {\n\t\tif (d->focus != NULL) {\n\t\t\td->focus = NULL;\n\t\t\tdraw_border(n, false, (mon == m));\n\t\t}\n\t\tif (d == mon->desk) {\n\t\t\tfocus_node(m, d, d->focus);\n\t\t} else {\n\t\t\tactivate_node(m, d, d->focus);\n\t\t}\n\t}\n\n\tif (single_monocle) {\n\t\tif (value && d->layout != LAYOUT_MONOCLE && tiled_count(d->root, true) <= 1) {\n\t\t\tset_layout(m, d, LAYOUT_MONOCLE, false);\n\t\t} else if (!value && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) {\n\t\t\tset_layout(m, d, d->user_layout, false);\n\t\t}\n\t}\n}\n\nvoid set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n->hidden == value) {\n\t\treturn;\n\t}\n\n\tn->hidden = value;\n\n\tif (n->client != NULL) {\n\t\tif (n->client->shown) {\n\t\t\twindow_set_visibility(n->id, !value);\n\t\t}\n\n\t\tif (IS_TILED(n->client)) {\n\t\t\tset_vacant(m, d, n, value);\n\t\t}\n\n\t\tif (value) {\n\t\t\tn->client->wm_flags |= WM_FLAG_HIDDEN;\n\t\t} else {\n\t\t\tn->client->wm_flags &= ~WM_FLAG_HIDDEN;\n\t\t}\n\n\t\tewmh_wm_state_update(n);\n\t}\n}\n\nvoid propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tset_hidden_local(m, d, n, value);\n\n\tpropagate_hidden_downward(m, d, n->first_child, value);\n\tpropagate_hidden_downward(m, d, n->second_child, value);\n}\n\nvoid propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tnode_t *p = n->parent;\n\n\tif (p != NULL) {\n\t\tset_hidden_local(m, d, p, p->first_child->hidden && p->second_child->hidden);\n\t}\n\n\tpropagate_hidden_upward(m, d, p);\n}\n\nvoid set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL || n->sticky == value) {\n\t\treturn;\n\t}\n\n\tif (d != m->desk) {\n\t\ttransfer_node(m, d, n, m, m->desk, m->desk->focus, false);\n\t}\n\n\tn->sticky = value;\n\n\tif (value) {\n\t\tm->sticky_count++;\n\t} else {\n\t\tm->sticky_count--;\n\t}\n\n\tif (n->client != NULL) {\n\t\tif (value) {\n\t\t\tn->client->wm_flags |= WM_FLAG_STICKY;\n\t\t} else {\n\t\t\tn->client->wm_flags &= ~WM_FLAG_STICKY;\n\t\t}\n\t\tewmh_wm_state_update(n);\n\t}\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X sticky %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\n\tif (n == m->desk->focus) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n}\n\nvoid set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL || n->private == value) {\n\t\treturn;\n\t}\n\n\tn->private = value;\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X private %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\n\tif (n == m->desk->focus) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n}\n\nvoid set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL || n->locked == value) {\n\t\treturn;\n\t}\n\n\tn->locked = value;\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X locked %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\n\tif (n == m->desk->focus) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n}\n\nvoid set_marked(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (n == NULL || n->marked == value) {\n\t\treturn;\n\t}\n\n\tn->marked = value;\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X marked %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\n\tif (n == m->desk->focus) {\n\t\tput_status(SBSC_MASK_REPORT);\n\t}\n}\n\nvoid set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)\n{\n\tif (value && mon->desk->focus == n) {\n\t\treturn;\n\t}\n\n\tn->client->urgent = value;\n\n\tif (value) {\n\t\tn->client->wm_flags |= WM_FLAG_DEMANDS_ATTENTION;\n\t} else {\n\t\tn->client->wm_flags &= ~WM_FLAG_DEMANDS_ATTENTION;\n\t}\n\n\tewmh_wm_state_update(n);\n\n\tput_status(SBSC_MASK_NODE_FLAG, \"node_flag 0x%08X 0x%08X 0x%08X urgent %s\\n\", m->id, d->id, n->id, ON_OFF_STR(value));\n\tput_status(SBSC_MASK_REPORT);\n}\n\nxcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn m->rectangle;\n\t}\n\tclient_t *c = n->client;\n\tif (c != NULL) {\n\t\tif (IS_FLOATING(c)) {\n\t\t\treturn c->floating_rectangle;\n\t\t} else {\n\t\t\treturn c->tiled_rectangle;\n\t\t}\n\t} else {\n\t\tint wg = (d == NULL ? 0 : (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap));\n\t\txcb_rectangle_t rect = n->rectangle;\n\t\trect.width -= wg;\n\t\trect.height -= wg;\n\t\treturn rect;\n\t}\n}\n\nvoid listen_enter_notify(node_t *n, bool enable)\n{\n\tuint32_t mask = CLIENT_EVENT_MASK | (enable ? XCB_EVENT_MASK_ENTER_WINDOW : 0);\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\txcb_change_window_attributes(dpy, f->id, XCB_CW_EVENT_MASK, &mask);\n\t\tif (f->presel != NULL) {\n\t\t\txcb_change_window_attributes(dpy, f->presel->feedback, XCB_CW_EVENT_MASK, &mask);\n\t\t}\n\t}\n}\n\nvoid regenerate_ids_in(node_t *n)\n{\n\tif (n == NULL || n->client != NULL) {\n\t\treturn;\n\t}\n\tn->id = xcb_generate_id(dpy);\n\tregenerate_ids_in(n->first_child);\n\tregenerate_ids_in(n->second_child);\n}\n\n#define DEF_FLAG_COUNT(flag) \\\n\tunsigned int flag##_count(node_t *n) \\\n\t{ \\\n\t\tif (n == NULL) { \\\n\t\t\treturn 0; \\\n\t\t} else { \\\n\t\t\treturn ((n->flag ? 1 : 0) + \\\n\t\t\t        flag##_count(n->first_child) + \\\n\t\t\t        flag##_count(n->second_child)); \\\n\t\t} \\\n\t}\n\tDEF_FLAG_COUNT(sticky)\n\tDEF_FLAG_COUNT(private)\n\tDEF_FLAG_COUNT(locked)\n#undef DEF_FLAG_COUNT\n"
  },
  {
    "path": "src/tree.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_TREE_H\n#define BSPWM_TREE_H\n\n#define MIN_WIDTH   32\n#define MIN_HEIGHT  32\n\nvoid arrange(monitor_t *m, desktop_t *d);\nvoid apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect);\npresel_t *make_presel(void);\nbool set_type(node_t *n, split_type_t typ);\nbool set_ratio(node_t *n, double rat);\nvoid presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);\nvoid presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);\nvoid cancel_presel(monitor_t *m, desktop_t *d, node_t *n);\nvoid cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);\nnode_t *find_public(desktop_t *d);\nnode_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);\nvoid insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);\nbool activate_node(monitor_t *m, desktop_t *d, node_t *n);\nvoid transfer_sticky_nodes(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n);\nbool focus_node(monitor_t *m, desktop_t *d, node_t *n);\nvoid hide_node(desktop_t *d, node_t *n);\nvoid show_node(desktop_t *d, node_t *n);\nnode_t *make_node(uint32_t id);\nclient_t *make_client(void);\nvoid initialize_client(node_t *n);\nbool is_focusable(node_t *n);\nbool is_leaf(node_t *n);\nbool is_first_child(node_t *n);\nbool is_second_child(node_t *n);\nunsigned int clients_count_in(node_t *n);\nnode_t *brother_tree(node_t *n);\nnode_t *first_extrema(node_t *n);\nnode_t *second_extrema(node_t *n);\nnode_t *first_focusable_leaf(node_t *n);\nnode_t *next_node(node_t *n);\nnode_t *prev_node(node_t *n);\nnode_t *next_leaf(node_t *n, node_t *r);\nnode_t *prev_leaf(node_t *n, node_t *r);\nnode_t *next_tiled_leaf(node_t *n, node_t *r);\nnode_t *prev_tiled_leaf(node_t *n, node_t *r);\nbool is_adjacent(node_t *a, node_t *b, direction_t dir);\nnode_t *find_fence(node_t *n, direction_t dir);\nbool is_child(node_t *a, node_t *b);\nbool is_descendant(node_t *a, node_t *b);\nbool find_by_id(uint32_t id, coordinates_t *loc);\nnode_t *find_by_id_in(node_t *r, uint32_t id);\nvoid find_any_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nbool find_any_node_in(monitor_t *m, desktop_t *d, node_t *n, coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nvoid find_first_ancestor(coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nvoid find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t *sel);\nunsigned int node_area(desktop_t *d, node_t *n);\nint tiled_count(node_t *n, bool include_receptacles);\nvoid find_by_area(area_peak_t ap, coordinates_t *ref, coordinates_t *dst, node_select_t *sel);\nvoid rotate_tree(node_t *n, int deg);\nvoid rotate_tree_rec(node_t *n, int deg);\nvoid flip_tree(node_t *n, flip_t flp);\nvoid equalize_tree(node_t *n);\nint balance_tree(node_t *n);\nvoid adjust_ratios(node_t *n, xcb_rectangle_t rect);\nvoid unlink_node(monitor_t *m, desktop_t *d, node_t *n);\nvoid close_node(node_t *n);\nvoid kill_node(monitor_t *m, desktop_t *d, node_t *n);\nvoid remove_node(monitor_t *m, desktop_t *d, node_t *n);\nvoid free_node(node_t *n);\nbool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2, bool follow);\nbool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd, bool follow);\nbool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t *sel);\nvoid circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);\nvoid set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);\nbool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);\nbool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);\nvoid set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);\nvoid rebuild_constraints_from_leaves(node_t *n);\nvoid rebuild_constraints_towards_root(node_t *n);\nvoid update_constraints(node_t *n);\nvoid propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);\nvoid set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);\nvoid set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_marked(monitor_t *m, desktop_t *d, node_t *n, bool value);\nvoid set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);\nxcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n);\nvoid listen_enter_notify(node_t *n, bool enable);\nvoid regenerate_ids_in(node_t *n);\n\nunsigned int sticky_count(node_t *n);\nunsigned int private_count(node_t *n);\nunsigned int locked_count(node_t *n);\n\n#endif\n"
  },
  {
    "path": "src/types.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_TYPES_H\n#define BSPWM_TYPES_H\n#include <stdbool.h>\n#include <xcb/xcb.h>\n#include <xcb/xcb_icccm.h>\n#include <xcb/randr.h>\n#include <xcb/xcb_event.h>\n#include \"helpers.h\"\n\n#define MISSING_VALUE        \"N/A\"\n#define MAX_WM_STATES        4\n\ntypedef enum {\n\tTYPE_HORIZONTAL,\n\tTYPE_VERTICAL\n} split_type_t;\n\ntypedef enum {\n\tMODE_AUTOMATIC,\n\tMODE_MANUAL\n} split_mode_t;\n\ntypedef enum {\n\tSCHEME_LONGEST_SIDE,\n\tSCHEME_ALTERNATE,\n\tSCHEME_SPIRAL\n} automatic_scheme_t;\n\ntypedef enum {\n\tHONOR_SIZE_HINTS_NO = 0,\n\tHONOR_SIZE_HINTS_YES,\n\tHONOR_SIZE_HINTS_FLOATING,\n\tHONOR_SIZE_HINTS_TILED,\n\tHONOR_SIZE_HINTS_DEFAULT\n} honor_size_hints_mode_t;\n\ntypedef enum {\n\tSTATE_TILED,\n\tSTATE_PSEUDO_TILED,\n\tSTATE_FLOATING,\n\tSTATE_FULLSCREEN\n} client_state_t;\n\ntypedef enum {\n\tWM_FLAG_MODAL = 1 << 0,\n\tWM_FLAG_STICKY = 1 << 1,\n\tWM_FLAG_MAXIMIZED_VERT = 1 << 2,\n\tWM_FLAG_MAXIMIZED_HORZ = 1 << 3,\n\tWM_FLAG_SHADED = 1 << 4,\n\tWM_FLAG_SKIP_TASKBAR = 1 << 5,\n\tWM_FLAG_SKIP_PAGER = 1 << 6,\n\tWM_FLAG_HIDDEN = 1 << 7,\n\tWM_FLAG_FULLSCREEN = 1 << 8,\n\tWM_FLAG_ABOVE = 1 << 9,\n\tWM_FLAG_BELOW = 1 << 10,\n\tWM_FLAG_DEMANDS_ATTENTION = 1 << 11,\n} wm_flags_t;\n\ntypedef enum {\n\tLAYER_BELOW,\n\tLAYER_NORMAL,\n\tLAYER_ABOVE\n} stack_layer_t;\n\ntypedef enum {\n\tOPTION_NONE,\n\tOPTION_TRUE,\n\tOPTION_FALSE\n} option_bool_t;\n\ntypedef enum {\n\tALTER_TOGGLE,\n\tALTER_SET\n} alter_state_t;\n\ntypedef enum {\n\tCYCLE_NEXT,\n\tCYCLE_PREV\n} cycle_dir_t;\n\ntypedef enum {\n\tCIRCULATE_FORWARD,\n\tCIRCULATE_BACKWARD\n} circulate_dir_t;\n\ntypedef enum {\n\tHISTORY_OLDER,\n\tHISTORY_NEWER\n} history_dir_t;\n\ntypedef enum {\n\tDIR_NORTH,\n\tDIR_WEST,\n\tDIR_SOUTH,\n\tDIR_EAST\n} direction_t;\n\ntypedef enum {\n\tHANDLE_LEFT = 1 << 0,\n\tHANDLE_TOP = 1 << 1,\n\tHANDLE_RIGHT = 1 << 2,\n\tHANDLE_BOTTOM = 1 << 3,\n\tHANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,\n\tHANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,\n\tHANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,\n\tHANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT\n} resize_handle_t;\n\ntypedef enum {\n\tACTION_NONE,\n\tACTION_FOCUS,\n\tACTION_MOVE,\n\tACTION_RESIZE_SIDE,\n\tACTION_RESIZE_CORNER\n} pointer_action_t;\n\ntypedef enum {\n\tLAYOUT_TILED,\n\tLAYOUT_MONOCLE\n} layout_t;\n\ntypedef enum {\n\tFLIP_HORIZONTAL,\n\tFLIP_VERTICAL\n} flip_t;\n\ntypedef enum {\n\tFIRST_CHILD,\n\tSECOND_CHILD\n} child_polarity_t;\n\ntypedef enum {\n\tTIGHTNESS_LOW,\n\tTIGHTNESS_HIGH,\n} tightness_t;\n\ntypedef enum {\n\tAREA_BIGGEST,\n\tAREA_SMALLEST,\n} area_peak_t;\n\ntypedef enum {\n\tSTATE_TRANSITION_ENTER = 1 << 0,\n\tSTATE_TRANSITION_EXIT = 1 << 1,\n} state_transition_t;\n\ntypedef struct {\n\toption_bool_t automatic;\n\toption_bool_t focused;\n\toption_bool_t active;\n\toption_bool_t local;\n\toption_bool_t leaf;\n\toption_bool_t window;\n\toption_bool_t tiled;\n\toption_bool_t pseudo_tiled;\n\toption_bool_t floating;\n\toption_bool_t fullscreen;\n\toption_bool_t hidden;\n\toption_bool_t sticky;\n\toption_bool_t private;\n\toption_bool_t locked;\n\toption_bool_t marked;\n\toption_bool_t urgent;\n\toption_bool_t same_class;\n\toption_bool_t descendant_of;\n\toption_bool_t ancestor_of;\n\toption_bool_t below;\n\toption_bool_t normal;\n\toption_bool_t above;\n\toption_bool_t horizontal;\n\toption_bool_t vertical;\n} node_select_t;\n\ntypedef struct {\n\toption_bool_t occupied;\n\toption_bool_t focused;\n\toption_bool_t active;\n\toption_bool_t urgent;\n\toption_bool_t local;\n\toption_bool_t tiled;\n\toption_bool_t monocle;\n\toption_bool_t user_tiled;\n\toption_bool_t user_monocle;\n} desktop_select_t;\n\ntypedef struct {\n\toption_bool_t occupied;\n\toption_bool_t focused;\n} monitor_select_t;\n\ntypedef struct icccm_props_t icccm_props_t;\nstruct icccm_props_t {\n\tbool take_focus;\n\tbool input_hint;\n\tbool delete_window;\n};\n\ntypedef struct {\n\tchar class_name[MAXLEN];\n\tchar instance_name[MAXLEN];\n\tchar name[MAXLEN];\n\tunsigned int border_width;\n\tbool urgent;\n\tbool shown;\n\tclient_state_t state;\n\tclient_state_t last_state;\n\tstack_layer_t layer;\n\tstack_layer_t last_layer;\n\txcb_rectangle_t floating_rectangle;\n\txcb_rectangle_t tiled_rectangle;\n\thonor_size_hints_mode_t honor_size_hints;\n\txcb_size_hints_t size_hints;\n\ticccm_props_t icccm_props;\n\twm_flags_t wm_flags;\n} client_t;\n\ntypedef struct presel_t presel_t;\nstruct presel_t {\n\tdouble split_ratio;\n\tdirection_t split_dir;\n\txcb_window_t feedback;\n};\n\ntypedef struct constraints_t constraints_t;\nstruct constraints_t {\n\tuint16_t min_width;\n\tuint16_t min_height;\n};\n\ntypedef struct node_t node_t;\nstruct node_t {\n\tuint32_t id;\n\tsplit_type_t split_type;\n\tdouble split_ratio;\n\tpresel_t *presel;\n\txcb_rectangle_t rectangle;\n\tconstraints_t constraints;\n\tbool vacant;\n\tbool hidden;\n\tbool sticky;\n\tbool private;\n\tbool locked;\n\tbool marked;\n\tnode_t *first_child;\n\tnode_t *second_child;\n\tnode_t *parent;\n\tclient_t *client;\n};\n\ntypedef struct padding_t padding_t;\nstruct padding_t {\n\tint top;\n\tint right;\n\tint bottom;\n\tint left;\n};\n\ntypedef struct desktop_t desktop_t;\nstruct desktop_t {\n\tchar name[SMALEN];\n\tuint32_t id;\n\tlayout_t layout;\n\tlayout_t user_layout;\n\tnode_t *root;\n\tnode_t *focus;\n\tdesktop_t *prev;\n\tdesktop_t *next;\n\tpadding_t padding;\n\tint window_gap;\n\tunsigned int border_width;\n};\n\ntypedef struct monitor_t monitor_t;\nstruct monitor_t {\n\tchar name[SMALEN];\n\tuint32_t id;\n\txcb_randr_output_t randr_id;\n\txcb_window_t root;\n\tbool wired;\n\tpadding_t padding;\n\tunsigned int sticky_count;\n\tint window_gap;\n\tunsigned int border_width;\n\txcb_rectangle_t rectangle;\n\tdesktop_t *desk;\n\tdesktop_t *desk_head;\n\tdesktop_t *desk_tail;\n\tmonitor_t *prev;\n\tmonitor_t *next;\n};\n\ntypedef struct {\n\tmonitor_t *monitor;\n\tdesktop_t *desktop;\n\tnode_t *node;\n} coordinates_t;\n\ntypedef struct history_t history_t;\nstruct history_t {\n\tcoordinates_t loc;\n\tbool latest;\n\thistory_t *prev;\n\thistory_t *next;\n};\n\ntypedef struct stacking_list_t stacking_list_t;\nstruct stacking_list_t {\n\tnode_t *node;\n\tstacking_list_t *prev;\n\tstacking_list_t *next;\n};\n\ntypedef struct event_queue_t event_queue_t;\nstruct event_queue_t {\n\txcb_generic_event_t event;\n\tevent_queue_t *prev;\n\tevent_queue_t *next;\n};\n\ntypedef struct subscriber_list_t subscriber_list_t;\nstruct subscriber_list_t {\n\tFILE *stream;\n\tchar* fifo_path;\n\tint field;\n\tint count;\n\tsubscriber_list_t *prev;\n\tsubscriber_list_t *next;\n};\n\ntypedef struct rule_t rule_t;\nstruct rule_t {\n\tchar class_name[MAXLEN];\n\tchar instance_name[MAXLEN];\n\tchar name[MAXLEN];\n\tchar effect[MAXLEN];\n\tbool one_shot;\n\trule_t *prev;\n\trule_t *next;\n};\n\ntypedef struct {\n\tchar class_name[MAXLEN];\n\tchar instance_name[MAXLEN];\n\tchar name[MAXLEN];\n\tchar monitor_desc[MAXLEN];\n\tchar desktop_desc[MAXLEN];\n\tchar node_desc[MAXLEN];\n\tdirection_t *split_dir;\n\tdouble split_ratio;\n\tstack_layer_t *layer;\n\tclient_state_t *state;\n\thonor_size_hints_mode_t honor_size_hints;\n\tbool hidden;\n\tbool sticky;\n\tbool private;\n\tbool locked;\n\tbool marked;\n\tbool center;\n\tbool follow;\n\tbool manage;\n\tbool focus;\n\tbool border;\n\txcb_rectangle_t *rect;\n} rule_consequence_t;\n\ntypedef struct pending_rule_t pending_rule_t;\nstruct pending_rule_t {\n\tint fd;\n\txcb_window_t win;\n\trule_consequence_t *csq;\n\tevent_queue_t *event_head;\n\tevent_queue_t *event_tail;\n\tpending_rule_t *prev;\n\tpending_rule_t *next;\n};\n\n#endif\n"
  },
  {
    "path": "src/window.c",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <xcb/shape.h>\n#include \"bspwm.h\"\n#include \"ewmh.h\"\n#include \"monitor.h\"\n#include \"desktop.h\"\n#include \"query.h\"\n#include \"rule.h\"\n#include \"settings.h\"\n#include \"geometry.h\"\n#include \"pointer.h\"\n#include \"stack.h\"\n#include \"tree.h\"\n#include \"parse.h\"\n#include \"window.h\"\n\nvoid schedule_window(xcb_window_t win)\n{\n\tcoordinates_t loc;\n\tuint8_t override_redirect = 0;\n\txcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);\n\n\tif (wa != NULL) {\n\t\toverride_redirect = wa->override_redirect;\n\t\tfree(wa);\n\t}\n\n\tif (override_redirect || locate_window(win, &loc)) {\n\t\treturn;\n\t}\n\n\t/* ignore pending windows */\n\tfor (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {\n\t\tif (pr->win == win) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\trule_consequence_t *csq = make_rule_consequence();\n\tapply_rules(win, csq);\n\tif (!schedule_rules(win, csq)) {\n\t\tmanage_window(win, csq, -1);\n\t\tfree(csq);\n\t}\n}\n\nbool manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)\n{\n\tmonitor_t *m = mon;\n\tdesktop_t *d = mon->desk;\n\tnode_t *f = mon->desk->focus;\n\n\tparse_rule_consequence(fd, csq);\n\n\tif (!ignore_ewmh_struts && ewmh_handle_struts(win)) {\n\t\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\t\tarrange(m, d);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!csq->manage) {\n\t\tfree(csq->layer);\n\t\tfree(csq->state);\n\t\twindow_show(win);\n\t\treturn false;\n\t}\n\n\tif (csq->node_desc[0] != '\\0') {\n\t\tcoordinates_t ref = {m, d, f};\n\t\tcoordinates_t trg = {NULL, NULL, NULL};\n\t\tif (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {\n\t\t\tm = trg.monitor;\n\t\t\td = trg.desktop;\n\t\t\tf = trg.node;\n\t\t}\n\t} else if (csq->desktop_desc[0] != '\\0') {\n\t\tcoordinates_t ref = {m, d, NULL};\n\t\tcoordinates_t trg = {NULL, NULL, NULL};\n\t\tif (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {\n\t\t\tm = trg.monitor;\n\t\t\td = trg.desktop;\n\t\t\tf = trg.desktop->focus;\n\t\t}\n\t} else if (csq->monitor_desc[0] != '\\0') {\n\t\tcoordinates_t ref = {m, NULL, NULL};\n\t\tcoordinates_t trg = {NULL, NULL, NULL};\n\t\tif (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {\n\t\t\tm = trg.monitor;\n\t\t\td = trg.monitor->desk;\n\t\t\tf = trg.monitor->desk->focus;\n\t\t}\n\t}\n\n\tif (csq->sticky) {\n\t\tm = mon;\n\t\td = mon->desk;\n\t\tf = mon->desk->focus;\n\t}\n\n\tif (csq->split_dir != NULL && f != NULL) {\n\t\tpresel_dir(m, d, f, *csq->split_dir);\n\t}\n\n\tif (csq->split_ratio != 0 && f != NULL) {\n\t\tpresel_ratio(m, d, f, csq->split_ratio);\n\t}\n\n\tnode_t *n = make_node(win);\n\tclient_t *c = make_client();\n\tc->border_width = csq->border ? d->border_width : 0;\n\tn->client = c;\n\tinitialize_client(n);\n\tinitialize_floating_rectangle(n);\n\n\tif (csq->rect != NULL) {\n\t\tc->floating_rectangle = *csq->rect;\n\t\tfree(csq->rect);\n\t} else if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {\n\t\tcsq->center = true;\n\t}\n\n\tmonitor_t *mm = monitor_from_client(c);\n\tembrace_client(mm, c);\n\tadapt_geometry(&mm->rectangle, &m->rectangle, n);\n\n\tif (csq->center) {\n\t\twindow_center(m, c);\n\t}\n\n\tsnprintf(c->class_name, sizeof(c->class_name), \"%s\", csq->class_name);\n\tsnprintf(c->instance_name, sizeof(c->instance_name), \"%s\", csq->instance_name);\n\n\tif ((csq->state != NULL && (*(csq->state) == STATE_FLOATING || *(csq->state) == STATE_FULLSCREEN)) || csq->hidden) {\n\t\tn->vacant = true;\n\t}\n\n\tf = insert_node(m, d, n, f);\n\tclients_count++;\n\tif (single_monocle && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) {\n\t\tset_layout(m, d, d->user_layout, false);\n\t}\n\n\tn->vacant = false;\n\n\tput_status(SBSC_MASK_NODE_ADD, \"node_add 0x%08X 0x%08X 0x%08X 0x%08X\\n\", m->id, d->id, f!=NULL?f->id:0, win);\n\n\tif (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {\n\t\tc->layer = f->client->layer;\n\t}\n\n\tif (csq->layer != NULL) {\n\t\tc->layer = *(csq->layer);\n\t}\n\n\tif (csq->state != NULL) {\n\t\tset_state(m, d, n, *(csq->state));\n\t}\n\n\tif (csq->honor_size_hints != HONOR_SIZE_HINTS_DEFAULT) {\n\t\tc->honor_size_hints = csq->honor_size_hints;\n\t}\n\n\tset_hidden(m, d, n, csq->hidden);\n\tset_sticky(m, d, n, csq->sticky);\n\tset_private(m, d, n, csq->private);\n\tset_locked(m, d, n, csq->locked);\n\tset_marked(m, d, n, csq->marked);\n\n\tarrange(m, d);\n\n\tuint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};\n\txcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);\n\tset_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);\n\twindow_grab_buttons(win);\n\n\tif (d == m->desk) {\n\t\tshow_node(d, n);\n\t} else {\n\t\thide_node(d, n);\n\t}\n\n\tewmh_update_client_list(false);\n\tewmh_set_wm_desktop(n, d);\n\n\tif (!csq->hidden && csq->focus) {\n\t\tif (d == mon->desk || csq->follow) {\n\t\t\tfocus_node(m, d, n);\n\t\t} else {\n\t\t\tactivate_node(m, d, n);\n\t\t}\n\t} else {\n\t\tstack(d, n, false);\n\t\tdraw_border(n, false, (m == mon));\n\t}\n\n\tfree(csq->layer);\n\tfree(csq->state);\n\n\treturn true;\n}\n\nvoid set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state)\n{\n\tlong data[] = {state, XCB_NONE};\n\txcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data);\n}\n\nvoid unmanage_window(xcb_window_t win)\n{\n\tcoordinates_t loc;\n\tif (locate_window(win, &loc)) {\n\t\tput_status(SBSC_MASK_NODE_REMOVE, \"node_remove 0x%08X 0x%08X 0x%08X\\n\", loc.monitor->id, loc.desktop->id, win);\n\t\tremove_node(loc.monitor, loc.desktop, loc.node);\n\t\tarrange(loc.monitor, loc.desktop);\n\t} else {\n\t\tfor (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {\n\t\t\tif (pr->win == win) {\n\t\t\t\tremove_pending_rule(pr);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool is_presel_window(xcb_window_t win)\n{\n\txcb_icccm_get_wm_class_reply_t reply;\n\tbool ret = false;\n\tif (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {\n\t\tif (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {\n\t\t\tret = true;\n\t\t}\n\t\txcb_icccm_get_wm_class_reply_wipe(&reply);\n\t}\n\treturn ret;\n}\n\nvoid initialize_presel_feedback(node_t *n)\n{\n\tif (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {\n\t\treturn;\n\t}\n\n\txcb_window_t win = xcb_generate_id(dpy);\n\tuint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER;\n\tuint32_t values[] = {get_color_pixel(presel_feedback_color), 1};\n\txcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,\n\t\t\t          XCB_COPY_FROM_PARENT, mask, values);\n\n\txcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);\n\t/* Make presel window's input shape NULL to pass any input to window below */\n\txcb_shape_rectangles(dpy, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, win, 0, 0, 0, NULL);\n\tstacking_list_t *s = stack_tail;\n\twhile (s != NULL && !IS_TILED(s->node->client)) {\n\t\ts = s->prev;\n\t}\n\tif (s != NULL) {\n\t\twindow_above(win, s->node->id);\n\t}\n\tn->presel->feedback = win;\n}\n\nvoid draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL || n->presel == NULL || d->user_layout == LAYOUT_MONOCLE || !presel_feedback) {\n\t\treturn;\n\t}\n\n\tbool exists = (n->presel->feedback != XCB_NONE);\n\tif (!exists) {\n\t\tinitialize_presel_feedback(n);\n\t}\n\n\tint gap = gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap;\n\tpresel_t *p = n->presel;\n\txcb_rectangle_t rect = n->rectangle;\n\trect.x = rect.y = 0;\n\trect.width -= gap;\n\trect.height -= gap;\n\txcb_rectangle_t presel_rect = rect;\n\n\tswitch (p->split_dir) {\n\t\tcase DIR_NORTH:\n\t\t\tpresel_rect.height = p->split_ratio * rect.height;\n\t\t\tbreak;\n\t\tcase DIR_EAST:\n\t\t\tpresel_rect.width = (1 - p->split_ratio) * rect.width;\n\t\t\tpresel_rect.x = rect.width - presel_rect.width;\n\t\t\tbreak;\n\t\tcase DIR_SOUTH:\n\t\t\tpresel_rect.height = (1 - p->split_ratio) * rect.height;\n\t\t\tpresel_rect.y = rect.height - presel_rect.height;\n\t\t\tbreak;\n\t\tcase DIR_WEST:\n\t\t\tpresel_rect.width = p->split_ratio * rect.width;\n\t\t\tbreak;\n\t}\n\n\twindow_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,\n\t                   presel_rect.width, presel_rect.height);\n\n\tif (!exists && m->desk == d) {\n\t\twindow_show(p->feedback);\n\t}\n}\n\nvoid refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (n->presel != NULL) {\n\t\t\tdraw_presel_feedback(m, d, n);\n\t\t}\n\t\trefresh_presel_feedbacks(m, d, n->first_child);\n\t\trefresh_presel_feedbacks(m, d, n->second_child);\n\t}\n}\n\nvoid show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (n->presel != NULL) {\n\t\t\twindow_show(n->presel->feedback);\n\t\t}\n\t\tshow_presel_feedbacks(m, d, n->first_child);\n\t\tshow_presel_feedbacks(m, d, n->second_child);\n\t}\n}\n\nvoid hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (n->presel != NULL) {\n\t\t\twindow_hide(n->presel->feedback);\n\t\t}\n\t\thide_presel_feedbacks(m, d, n->first_child);\n\t\thide_presel_feedbacks(m, d, n->second_child);\n\t}\n}\n\nvoid update_colors(void)\n{\n\tfor (monitor_t *m = mon_head; m != NULL; m = m->next) {\n\t\tfor (desktop_t *d = m->desk_head; d != NULL; d = d->next) {\n\t\t\tupdate_colors_in(d->root, d, m);\n\t\t}\n\t}\n}\n\nvoid update_colors_in(node_t *n, desktop_t *d, monitor_t *m)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t} else {\n\t\tif (n->presel != NULL) {\n\t\t\tuint32_t pxl = get_color_pixel(presel_feedback_color);\n\t\t\txcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);\n\t\t\tif (d == m->desk) {\n\t\t\t\t/* hack to induce back pixel refresh */\n\t\t\t\twindow_hide(n->presel->feedback);\n\t\t\t\twindow_show(n->presel->feedback);\n\t\t\t}\n\t\t}\n\t\tif (n == d->focus) {\n\t\t\tdraw_border(n, true, (m == mon));\n\t\t} else if (n->client != NULL) {\n\t\t\tdraw_border(n, false, (m == mon));\n\t\t} else {\n\t\t\tupdate_colors_in(n->first_child, d, m);\n\t\t\tupdate_colors_in(n->second_child, d, m);\n\t\t}\n\t}\n}\n\nvoid draw_border(node_t *n, bool focused_node, bool focused_monitor)\n{\n\tif (n == NULL) {\n\t\treturn;\n\t}\n\n\tuint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);\n\tfor (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {\n\t\tif (f->client != NULL) {\n\t\t\twindow_draw_border(f->id, border_color_pxl);\n\t\t}\n\t}\n}\n\nvoid window_draw_border(xcb_window_t win, uint32_t border_color_pxl)\n{\n\txcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);\n}\n\nvoid adopt_orphans(void)\n{\n\txcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);\n\tif (qtr == NULL) {\n\t\treturn;\n\t}\n\n\tint len = xcb_query_tree_children_length(qtr);\n\txcb_window_t *wins = xcb_query_tree_children(qtr);\n\n\tfor (int i = 0; i < len; i++) {\n\t\tuint32_t idx;\n\t\txcb_window_t win = wins[i];\n\t\tif (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {\n\t\t\tschedule_window(win);\n\t\t}\n\t}\n\n\tfree(qtr);\n}\n\nuint32_t get_border_color(bool focused_node, bool focused_monitor)\n{\n\tif (focused_monitor && focused_node) {\n\t\treturn get_color_pixel(focused_border_color);\n\t} else if (focused_node) {\n\t\treturn get_color_pixel(active_border_color);\n\t} else {\n\t\treturn get_color_pixel(normal_border_color);\n\t}\n}\n\nvoid initialize_floating_rectangle(node_t *n)\n{\n\tclient_t *c = n->client;\n\n\txcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);\n\n\tif (geo != NULL) {\n\t\tc->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};\n\t}\n\n\tfree(geo);\n}\n\nxcb_rectangle_t get_window_rectangle(node_t *n)\n{\n\tclient_t *c = n->client;\n\tif (c != NULL) {\n\t\txcb_get_geometry_reply_t *g = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);\n\t\tif (g != NULL) {\n\t\t\txcb_rectangle_t rect = (xcb_rectangle_t) {g->x, g->y, g->width, g->height};\n\t\t\tfree(g);\n\t\t\treturn rect;\n\t\t}\n\t}\n\treturn (xcb_rectangle_t) {0, 0, screen_width, screen_height};\n}\n\nbool move_client(coordinates_t *loc, int dx, int dy)\n{\n\tnode_t *n = loc->node;\n\n\tif (n == NULL || n->client == NULL) {\n\t\treturn false;\n\t}\n\n\tmonitor_t *pm = NULL;\n\n\tif (IS_TILED(n->client)) {\n\t\tif (!grabbing) {\n\t\t\treturn false;\n\t\t}\n\t\txcb_window_t pwin = XCB_NONE;\n\t\tquery_pointer(&pwin, NULL);\n\t\tif (pwin == n->id) {\n\t\t\treturn false;\n\t\t}\n\t\tcoordinates_t dst;\n\t\tbool is_managed = (pwin != XCB_NONE && locate_window(pwin, &dst));\n\t\tif (is_managed && dst.monitor == loc->monitor && IS_TILED(dst.node->client)) {\n\t\t\tswap_nodes(loc->monitor, loc->desktop, n, loc->monitor, loc->desktop, dst.node, false);\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (is_managed && dst.monitor == loc->monitor) {\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\txcb_point_t pt = {0, 0};\n\t\t\t\tquery_pointer(NULL, &pt);\n\t\t\t\tpm = monitor_from_point(pt);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tclient_t *c = n->client;\n\t\txcb_rectangle_t rect = c->floating_rectangle;\n\t\tint16_t x = rect.x + dx;\n\t\tint16_t y = rect.y + dy;\n\n\t\twindow_move(n->id, x, y);\n\n\t\tc->floating_rectangle.x = x;\n\t\tc->floating_rectangle.y = y;\n\t\tif (!grabbing) {\n\t\t\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", loc->monitor->id, loc->desktop->id, loc->node->id, rect.width, rect.height, x, y);\n\t\t}\n\t\tpm = monitor_from_client(c);\n\t}\n\n\tif (pm == NULL || pm == loc->monitor) {\n\t\treturn true;\n\t}\n\n\ttransfer_node(loc->monitor, loc->desktop, n, pm, pm->desk, pm->desk->focus, true);\n\tloc->monitor = pm;\n\tloc->desktop = pm->desk;\n\n\treturn true;\n}\n\nbool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy, bool relative)\n{\n\tnode_t *n = loc->node;\n\tif (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {\n\t\treturn false;\n\t}\n\tnode_t *horizontal_fence = NULL, *vertical_fence = NULL;\n\txcb_rectangle_t rect = get_rectangle(NULL, NULL, n);\n\tuint16_t width = rect.width, height = rect.height;\n\tint16_t x = rect.x, y = rect.y;\n\tif (n->client->state == STATE_TILED) {\n\t\tif (rh & HANDLE_LEFT) {\n\t\t\tvertical_fence = find_fence(n, DIR_WEST);\n\t\t} else if (rh & HANDLE_RIGHT) {\n\t\t\tvertical_fence = find_fence(n, DIR_EAST);\n\t\t}\n\t\tif (rh & HANDLE_TOP) {\n\t\t\thorizontal_fence = find_fence(n, DIR_NORTH);\n\t\t} else if (rh & HANDLE_BOTTOM) {\n\t\t\thorizontal_fence = find_fence(n, DIR_SOUTH);\n\t\t}\n\t\tif (vertical_fence == NULL && horizontal_fence == NULL) {\n\t\t\treturn false;\n\t\t}\n\t\tif (vertical_fence != NULL) {\n\t\t\tdouble sr = 0.0;\n\t\t\tif (relative) {\n\t\t\t\tsr = vertical_fence->split_ratio + (double) dx / (double) vertical_fence->rectangle.width;\n\t\t\t} else {\n\t\t\t\tsr = (double) (dx - vertical_fence->rectangle.x) / (double) vertical_fence->rectangle.width;\n\t\t\t}\n\t\t\tsr = MAX(0, sr);\n\t\t\tsr = MIN(1, sr);\n\t\t\tvertical_fence->split_ratio = sr;\n\t\t\tadjust_ratios(vertical_fence, vertical_fence->rectangle);\n\t\t}\n\t\tif (horizontal_fence != NULL) {\n\t\t\tdouble sr = 0.0;\n\t\t\tif (relative) {\n\t\t\t\tsr = horizontal_fence->split_ratio + (double) dy / (double) horizontal_fence->rectangle.height;\n\t\t\t} else {\n\t\t\t\tsr = (double) (dy - horizontal_fence->rectangle.y) / (double) horizontal_fence->rectangle.height;\n\t\t\t}\n\t\t\tsr = MAX(0, sr);\n\t\t\tsr = MIN(1, sr);\n\t\t\thorizontal_fence->split_ratio = sr;\n\t\t\tadjust_ratios(horizontal_fence, horizontal_fence->rectangle);\n\t\t}\n\t\tarrange(loc->monitor, loc->desktop);\n\t} else {\n\t\tint w = width, h = height;\n\t\tif (relative) {\n\t\t\tw += dx * (rh & HANDLE_LEFT ? -1 : (rh & HANDLE_RIGHT ? 1 : 0));\n\t\t\th += dy * (rh & HANDLE_TOP ? -1 : (rh & HANDLE_BOTTOM ? 1 : 0));\n\t\t} else {\n\t\t\tif (rh & HANDLE_LEFT) {\n\t\t\t\tw = x + width - dx;\n\t\t\t} else if (rh & HANDLE_RIGHT) {\n\t\t\t\tw = dx - x;\n\t\t\t}\n\t\t\tif (rh & HANDLE_TOP) {\n\t\t\t\th = y + height - dy;\n\t\t\t} else if (rh & HANDLE_BOTTOM) {\n\t\t\t\th = dy - y;\n\t\t\t}\n\t\t}\n\t\twidth = MAX(1, w);\n\t\theight = MAX(1, h);\n\t\tapply_size_hints(n->client, &width, &height);\n\t\tif (rh & HANDLE_LEFT) {\n\t\t\tx += rect.width - width;\n\t\t}\n\t\tif (rh & HANDLE_TOP) {\n\t\t\ty += rect.height - height;\n\t\t}\n\t\tn->client->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};\n\t\tif (n->client->state == STATE_FLOATING) {\n\t\t\twindow_move_resize(n->id, x, y, width, height);\n\n\t\t\tif (!grabbing) {\n\t\t\t\tput_status(SBSC_MASK_NODE_GEOMETRY, \"node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\\n\", loc->monitor->id, loc->desktop->id, loc->node->id, width, height, x, y);\n\t\t\t}\n\t\t} else {\n\t\t\tarrange(loc->monitor, loc->desktop);\n\t\t}\n\t}\n\treturn true;\n}\n\n/* taken from awesomeWM */\nvoid apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)\n{\n\tif (!SHOULD_HONOR_SIZE_HINTS(c->honor_size_hints, c->state)) {\n\t\treturn;\n\t}\n\n\tint32_t minw = 0, minh = 0;\n\tint32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;\n\n\tif (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {\n\t\tbasew = c->size_hints.base_width;\n\t\tbaseh = c->size_hints.base_height;\n\t\treal_basew = basew;\n\t\treal_baseh = baseh;\n\t} else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {\n\t\t/* base size is substituted with min size if not specified */\n\t\tbasew = c->size_hints.min_width;\n\t\tbaseh = c->size_hints.min_height;\n\t}\n\n\tif (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {\n\t\tminw = c->size_hints.min_width;\n\t\tminh = c->size_hints.min_height;\n\t} else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {\n\t\t/* min size is substituted with base size if not specified */\n\t\tminw = c->size_hints.base_width;\n\t\tminh = c->size_hints.base_height;\n\t}\n\n\t/* Handle the size aspect ratio */\n\tif (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT &&\n\t    c->size_hints.min_aspect_den > 0 &&\n\t    c->size_hints.max_aspect_den > 0 &&\n\t    *height > real_baseh &&\n\t    *width > real_basew) {\n\t\t/* ICCCM mandates:\n\t\t * If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the\n\t\t * window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing\n\t\t * should be subtracted from the window size. (The minimum size is not to be used in place of the base size for\n\t\t * this purpose.)\n\t\t */\n\t\tdouble dx = *width - real_basew;\n\t\tdouble dy = *height - real_baseh;\n\t\tdouble ratio = dx / dy;\n\t\tdouble min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;\n\t\tdouble max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;\n\n\t\tif (max > 0 && min > 0 && ratio > 0) {\n\t\t\tif (ratio < min) {\n\t\t\t\t/* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */\n\t\t\t\tdy = dx / min + 0.5;\n\t\t\t\t*width  = dx + real_basew;\n\t\t\t\t*height = dy + real_baseh;\n\t\t\t} else if (ratio > max) {\n\t\t\t\t/* dx is too high, lower it (+0.5 for proper rounding) */\n\t\t\t\tdx = dy * max + 0.5;\n\t\t\t\t*width  = dx + real_basew;\n\t\t\t\t*height = dy + real_baseh;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Handle the minimum size */\n\t*width = MAX(*width, minw);\n\t*height = MAX(*height, minh);\n\n\t/* Handle the maximum size */\n\tif (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)\n\t{\n\t\tif (c->size_hints.max_width > 0) {\n\t\t\t*width = MIN(*width, c->size_hints.max_width);\n\t\t}\n\t\tif (c->size_hints.max_height > 0) {\n\t\t\t*height = MIN(*height, c->size_hints.max_height);\n\t\t}\n\t}\n\n\t/* Handle the size increment */\n\tif (c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE) &&\n\t    c->size_hints.width_inc > 0 && c->size_hints.height_inc > 0) {\n\t\tuint16_t t1 = *width, t2 = *height;\n\t\tunsigned_subtract(t1, basew);\n\t\tunsigned_subtract(t2, baseh);\n\t\t*width -= t1 % c->size_hints.width_inc;\n\t\t*height -= t2 % c->size_hints.height_inc;\n\t}\n}\n\nvoid query_pointer(xcb_window_t *win, xcb_point_t *pt)\n{\n\tif (motion_recorder.enabled) {\n\t\twindow_hide(motion_recorder.id);\n\t}\n\n\txcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);\n\n\tif (qpr != NULL) {\n\t\tif (win != NULL) {\n\t\t\tif (qpr->child == XCB_NONE) {\n\t\t\t\txcb_point_t mpt = (xcb_point_t) {qpr->root_x, qpr->root_y};\n\t\t\t\tmonitor_t *m = monitor_from_point(mpt);\n\t\t\t\tif (m != NULL) {\n\t\t\t\t\tdesktop_t *d = m->desk;\n\t\t\t\t\tfor (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\t\t\t\t\tif (n->client == NULL && is_inside(mpt, get_rectangle(m, d, n))) {\n\t\t\t\t\t\t\t*win = n->id;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t*win = qpr->child;\n\t\t\t\txcb_point_t pt = {qpr->root_x, qpr->root_y};\n\t\t\t\tfor (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {\n\t\t\t\t\tif (!s->node->client->shown || s->node->hidden) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\txcb_rectangle_t rect = get_rectangle(NULL, NULL, s->node);\n\t\t\t\t\tif (is_inside(pt, rect)) {\n\t\t\t\t\t\tif (s->node->id == qpr->child || is_presel_window(qpr->child)) {\n\t\t\t\t\t\t\t*win = s->node->id;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pt != NULL) {\n\t\t\t*pt = (xcb_point_t) {qpr->root_x, qpr->root_y};\n\t\t}\n\t}\n\n\tfree(qpr);\n\n\tif (motion_recorder.enabled) {\n\t\twindow_show(motion_recorder.id);\n\t}\n}\n\nvoid update_motion_recorder(void)\n{\n\txcb_point_t pt;\n\txcb_window_t win = XCB_NONE;\n\tquery_pointer(&win, &pt);\n\tif (win == XCB_NONE) {\n\t\treturn;\n\t}\n\tmonitor_t *m = monitor_from_point(pt);\n\tif (m == NULL) {\n\t\treturn;\n\t}\n\tdesktop_t *d = m->desk;\n\tnode_t *n = NULL;\n\tfor (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {\n\t\tif (n->id == win || (n->presel != NULL && n->presel->feedback == win)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif ((n != NULL && n != mon->desk->focus) || (n == NULL && m != mon)) {\n\t\tenable_motion_recorder(win);\n\t} else {\n\t\tdisable_motion_recorder();\n\t}\n}\n\nvoid enable_motion_recorder(xcb_window_t win)\n{\n\txcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);\n\tif (geo != NULL) {\n\t\tuint16_t width = geo->width + 2 * geo->border_width;\n\t\tuint16_t height = geo->height + 2 * geo->border_width;\n\t\twindow_move_resize(motion_recorder.id, geo->x, geo->y, width, height);\n\t\twindow_above(motion_recorder.id, win);\n\t\twindow_show(motion_recorder.id);\n\t\tmotion_recorder.enabled = true;\n\t}\n\tfree(geo);\n}\n\nvoid disable_motion_recorder(void)\n{\n\tif (!motion_recorder.enabled) {\n\t\treturn;\n\t}\n\twindow_hide(motion_recorder.id);\n\tmotion_recorder.enabled = false;\n}\n\nvoid window_border_width(xcb_window_t win, uint32_t bw)\n{\n\tuint32_t values[] = {bw};\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);\n}\n\nvoid window_move(xcb_window_t win, int16_t x, int16_t y)\n{\n\tuint32_t values[] = {x, y};\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);\n}\n\nvoid window_resize(xcb_window_t win, uint16_t w, uint16_t h)\n{\n\tuint32_t values[] = {w, h};\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);\n}\n\nvoid window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)\n{\n\tuint32_t values[] = {x, y, w, h};\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);\n}\n\nvoid window_center(monitor_t *m, client_t *c)\n{\n\txcb_rectangle_t *r = &c->floating_rectangle;\n\txcb_rectangle_t a = m->rectangle;\n\tif (r->width >= a.width) {\n\t\tr->x = a.x;\n\t} else {\n\t\tr->x = a.x + (a.width - r->width) / 2;\n\t}\n\tif (r->height >= a.height) {\n\t\tr->y = a.y;\n\t} else {\n\t\tr->y = a.y + (a.height - r->height) / 2;\n\t}\n\tr->x -= c->border_width;\n\tr->y -= c->border_width;\n}\n\nvoid window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)\n{\n\tif (w2 == XCB_NONE) {\n\t\treturn;\n\t}\n\tuint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;\n\tuint32_t values[] = {w2, mode};\n\txcb_configure_window(dpy, w1, mask, values);\n}\n\n/* Stack w1 above w2 */\nvoid window_above(xcb_window_t w1, xcb_window_t w2)\n{\n\twindow_stack(w1, w2, XCB_STACK_MODE_ABOVE);\n}\n\n/* Stack w1 below w2 */\nvoid window_below(xcb_window_t w1, xcb_window_t w2)\n{\n\twindow_stack(w1, w2, XCB_STACK_MODE_BELOW);\n}\n\nvoid window_lower(xcb_window_t win)\n{\n\tuint32_t values[] = {XCB_STACK_MODE_BELOW};\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);\n}\n\nvoid window_set_visibility(xcb_window_t win, bool visible)\n{\n\tuint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};\n\tuint32_t values_on[] = {ROOT_EVENT_MASK};\n\txcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);\n\tif (visible) {\n\t\tset_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);\n\t\txcb_map_window(dpy, win);\n\t} else {\n\t\txcb_unmap_window(dpy, win);\n\t\tset_window_state(win, XCB_ICCCM_WM_STATE_ICONIC);\n\t}\n\txcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);\n}\n\nvoid window_hide(xcb_window_t win)\n{\n\twindow_set_visibility(win, false);\n}\n\nvoid window_show(xcb_window_t win)\n{\n\twindow_set_visibility(win, true);\n}\n\nvoid update_input_focus(void)\n{\n\tset_input_focus(mon->desk->focus);\n}\n\nvoid set_input_focus(node_t *n)\n{\n\tif (n == NULL || n->client == NULL) {\n\t\tclear_input_focus();\n\t} else {\n\t\tif (n->client->icccm_props.input_hint) {\n\t\t\txcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);\n\t\t} else if (n->client->icccm_props.take_focus) {\n\t\t\tsend_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);\n\t\t}\n\t}\n}\n\nvoid clear_input_focus(void)\n{\n\txcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);\n}\n\nvoid center_pointer(xcb_rectangle_t r)\n{\n\tif (grabbing) {\n\t\treturn;\n\t}\n\tint16_t cx = r.x + r.width / 2;\n\tint16_t cy = r.y + r.height / 2;\n\txcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);\n}\n\nvoid get_atom(char *name, xcb_atom_t *atom)\n{\n\txcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);\n\tif (reply != NULL) {\n\t\t*atom = reply->atom;\n\t} else {\n\t\t*atom = XCB_NONE;\n\t}\n\tfree(reply);\n}\n\nvoid set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)\n{\n\txcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);\n}\n\nvoid send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)\n{\n\txcb_client_message_event_t *e = calloc(32, 1);\n\n\te->response_type = XCB_CLIENT_MESSAGE;\n\te->window = win;\n\te->type = property;\n\te->format = 32;\n\te->data.data32[0] = value;\n\te->data.data32[1] = XCB_CURRENT_TIME;\n\n\txcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) e);\n\txcb_flush(dpy);\n\tfree(e);\n}\n\nbool window_exists(xcb_window_t win)\n{\n\txcb_generic_error_t *err;\n\tfree(xcb_query_tree_reply(dpy, xcb_query_tree(dpy, win), &err));\n\n\tif (err != NULL) {\n\t\tfree(err);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/window.h",
    "content": "/* Copyright (c) 2012, Bastien Dejean\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *    list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef BSPWM_WINDOW_H\n#define BSPWM_WINDOW_H\n\n#include <stdarg.h>\n#include <xcb/xcb.h>\n#include <xcb/xcb_event.h>\n#include <xcb/xcb_icccm.h>\n#include \"types.h\"\n\nvoid schedule_window(xcb_window_t win);\nbool manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);\nvoid set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state);\nvoid unmanage_window(xcb_window_t win);\nbool is_presel_window(xcb_window_t win);\nvoid initialize_presel_feedback(node_t *n);\nvoid draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);\nvoid refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);\nvoid show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);\nvoid hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);\nvoid update_colors(void);\nvoid update_colors_in(node_t *n, desktop_t *d, monitor_t *m);\nvoid draw_border(node_t *n, bool focused_node, bool focused_monitor);\nvoid window_draw_border(xcb_window_t win, uint32_t border_color_pxl);\nvoid adopt_orphans(void);\nuint32_t get_border_color(bool focused_node, bool focused_monitor);\nvoid initialize_floating_rectangle(node_t *n);\nxcb_rectangle_t get_window_rectangle(node_t *n);\nbool move_client(coordinates_t *loc, int dx, int dy);\nbool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy, bool relative);\nvoid apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);\nvoid query_pointer(xcb_window_t *win, xcb_point_t *pt);\nvoid update_motion_recorder(void);\nvoid enable_motion_recorder(xcb_window_t win);\nvoid disable_motion_recorder(void);\nvoid window_border_width(xcb_window_t win, uint32_t bw);\nvoid window_move(xcb_window_t win, int16_t x, int16_t y);\nvoid window_resize(xcb_window_t win, uint16_t w, uint16_t h);\nvoid window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);\nvoid window_center(monitor_t *m, client_t *c);\nvoid window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);\nvoid window_above(xcb_window_t w1, xcb_window_t w2);\nvoid window_below(xcb_window_t w1, xcb_window_t w2);\nvoid window_lower(xcb_window_t win);\nvoid window_set_visibility(xcb_window_t win, bool visible);\nvoid window_hide(xcb_window_t win);\nvoid window_show(xcb_window_t win);\nvoid update_input_focus(void);\nvoid set_input_focus(node_t *n);\nvoid clear_input_focus(void);\nvoid center_pointer(xcb_rectangle_t r);\nvoid get_atom(char *name, xcb_atom_t *atom);\nvoid set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);\nvoid send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);\nbool window_exists(xcb_window_t win);\n\n#endif\n"
  },
  {
    "path": "tests/Makefile",
    "content": "OUT      = test_window\nCFLAGS  += -std=c99 -pedantic -Wall -Wextra\nLDLIBS   = -lxcb -lxcb-icccm\n\nSRC = $(wildcard *.c)\nOBJ = $(SRC:.c=.o)\n\nall: $(OUT)\n\nclean:\n\t$(RM) $(OUT) $(OBJ)\n\n.PHONY: all clean\n"
  },
  {
    "path": "tests/README.md",
    "content": "- Install *jshon*.\n- Run `make` once.\n- Run `./run`.\n"
  },
  {
    "path": "tests/desktop/swap",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc wm -a \"TEST-SWAP-A\" 1024x512+0+0\nbspc wm -a \"TEST-SWAP-B\" 1024x512+0+512\n\nbspc monitor -f \"TEST-SWAP-A\"\nwindow add 3\n\nbspc monitor -f \"TEST-SWAP-B\"\nwindow add 2\n\nnodes_a=$(bspc query -N -m \"TEST-SWAP-A\")\nnodes_b=$(bspc query -N -m \"TEST-SWAP-B\")\n\nbspc desktop \"TEST-SWAP-A:^1\" -s \"TEST-SWAP-B:^1\"\n\n[ \"$(bspc query -N -m 'TEST-SWAP-A')\" = \"$nodes_b\" ] || fail \"Wrong nodes in first monitor\"\n[ \"$(bspc query -N -m 'TEST-SWAP-B')\" = \"$nodes_a\" ] || fail \"Wrong nodes in second monitor\"\n\nwindow remove 3\nbspc monitor -f \"TEST-SWAP-A\"\nwindow remove 2\n\nbspc monitor \"TEST-SWAP-A\" -r\nbspc monitor \"TEST-SWAP-B\" -r\n"
  },
  {
    "path": "tests/desktop/transfer",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc wm -a \"TEST-TRANSFER-A\" 1024x512+0+0\nbspc wm -a \"TEST-TRANSFER-B\" 1024x512+0+512\n\nbspc monitor \"TEST-TRANSFER-A\" -a source\nbspc monitor -f \"TEST-TRANSFER-A\"\n\nwindow add 3\n\nroot_rectangle_y=$(bspc query -T -n @/ | jshon -e rectangle -e y)\n\nbspc desktop \"TEST-TRANSFER-A:focused\" -m \"TEST-TRANSFER-B\"\n\n[ \"$(bspc query -D -m \"TEST-TRANSFER-A\" | wc -l)\" -eq 1 ] || fail \"Invalid number of desktop in source after transfer.\"\n\nbspc desktop \"TEST-TRANSFER-B:^2\" -f\n\n[ \"$(bspc query -T -n @/ | jshon -e rectangle -e y)\" -ne \"$root_rectangle_y\" ] || fail \"Wrong tiled rectangle for root in destination.\"\n\nwindow remove 3\n\nbspc monitor \"TEST-TRANSFER-A\" -r\nbspc monitor \"TEST-TRANSFER-B\" -r\n"
  },
  {
    "path": "tests/node/flags",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-sticky-a\"\nbspc monitor -a \"test-sticky-b\"\n\nbspc desktop -f \"test-sticky-a\"\n\nwindow add 3\nbspc node -g sticky\n\nsticky_node_id=$(bspc query -N -n)\n\nbspc rule -a 'Blah\\:\\:2:test' -o desktop=\"test-sticky-b\"\n\nCLASS_NAME=Blah::2 INSTANCE_NAME=test window add\n\nbspc desktop -f \"test-sticky-b\"\n\nbspc query -N -d | grep \"$sticky_node_id\" > /dev/null || fail \"Sticky node is missing in destination.\"\n\nwindow remove 2\nbspc desktop -f \"test-sticky-a\"\nwindow remove 2\n\nbspc desktop \"test-sticky-a\" -r\nbspc desktop \"test-sticky-b\" -r\n"
  },
  {
    "path": "tests/node/insertion",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-insertion\"\nbspc desktop -f \"test-insertion\"\n\n# Automatic mode\n\nwindow add 2\n\nsplit_type_a=$(bspc query -T -n @/ | jshon -e splitType -u)\n\nwindow add\n\nsplit_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u)\n\n[ \"$split_type_a\" = \"$split_type_b\" ] && fail \"Non-vacant node insertion should rotate brother.\"\n\nsplit_type_a=$(bspc query -T -n @/ | jshon -e splitType -u)\n\nbspc rule -a Test:test -o state=floating\nwindow add\n\nsplit_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u)\n\n[ \"$split_type_a\" = \"$split_type_b\" ] || fail \"Vacant node insertion shouldn't rotate brother.\"\n\nwindow remove\n\n# Manual mode\n\nfor dir in north west south east ; do\n\tchild=1\n\tsplit_type=vertical\n\t[ \"$dir\" = \"south\" -o \"$dir\" = \"east\" ] && child=2\n\t[ \"$dir\" = \"north\" -o \"$dir\" = \"south\" ] && split_type=horizontal\n\tbspc node -p $dir\n\twindow add\n\t[ \"$(bspc query -N -n)\" = \"$(bspc query -N -n @parent/${child})\" ] || fail \"Wrong child polarity for ${dir} preselection.\"\n\t[ \"$(bspc query -T -n @parent | jshon -e splitType -u)\" = \"$split_type\" ] || fail \"Wrong split type for ${dir} preselection.\"\ndone\n\nwindow remove 7\n\nbspc desktop \"test-insertion\" -r\n"
  },
  {
    "path": "tests/node/receptacle",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-receptacle\"\nbspc desktop -f \"test-receptacle\"\n\nbspc node -i\nbspc node @/ -p east -i\nbspc node @/2 -p north -i\n\nbspc rule -a Test:test -o node=@/1\nbspc rule -a Test:test -o node=@/2/1\nbspc rule -a Test:test -o node=@/2/2\n\nwindow add 3\n\nbspc query -N -n '.leaf.!window.local' > /dev/null && fail \"At least one remaining receptacle.\"\n\nwindow remove 3\n\nbspc desktop \"test-receptacle\" -r\n"
  },
  {
    "path": "tests/node/removal",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-removal\"\nbspc desktop -f \"test-removal\"\n\nwindow add 3\n\nnext_focus=$(bspc query -N -n);\n\nbspc node -f @/2/1\nbspc node @/2 -k\n\n[ \"$(bspc query -N -n)\" = \"$next_focus\" ] || fail \"Invalid focus after removal.\"\n\nwindow remove\n\nbspc desktop \"test-removal\" -r\n"
  },
  {
    "path": "tests/node/swap",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-swap-a\" \"test-swap-b\"\nbspc desktop -f \"test-swap-a\"\n\nwindow add 5\nnext_focus_b=$(bspc query -N -n @/2/2/1)\nbspc desktop -f \"test-swap-b\"\nwindow add 3\n\nbspc node -f @test-swap-a:/2/2/1\nbspc node -a @test-swap-b:/1\n\nbspc node @/2 -s @test-swap-b:/1\n\n[ \"$(bspc query -N -n @test-swap-b:)\" = \"$next_focus_b\" ] || fail \"Invalid focus after swap.\"\n\nwindow remove 2\nbspc desktop -f \"test-swap-b\"\nwindow remove 1 2\nwindow remove 4\n\nbspc desktop \"test-swap-a\" -r\nbspc desktop \"test-swap-b\" -r\n"
  },
  {
    "path": "tests/node/transfer",
    "content": "#! /bin/sh\n\n. ./prelude\n\nbspc monitor -a \"test-transfer-a\" \"test-transfer-b\"\nbspc desktop -f \"test-transfer-a\"\n\nwindow add 5\n\nnext_focus_a=$(bspc query -N -n @/1)\nnext_focus_b=$(bspc query -N -n @/2/2/1)\n\nbspc node -f $next_focus_b\nbspc node @/2 -d \"test-transfer-b\"\n\n[ \"$next_focus_a\" = \"$(bspc query -N -n @test-transfer-a:)\" ] || fail \"Invalid focus after transfer from source.\"\n[ \"$next_focus_b\" = \"$(bspc query -N -n @test-transfer-b:)\" ] || fail \"Invalid focus after transfer in destination.\"\n\nwindow remove\nbspc desktop -f \"test-transfer-b\"\nwindow remove 1 2\nwindow remove 2\n\nbspc desktop \"test-transfer-a\" -r\nbspc desktop \"test-transfer-b\" -r\n"
  },
  {
    "path": "tests/prelude",
    "content": "#! /bin/sh\n\nfail() {\n\techo \"$@\" 1>&2\n\texit 1\n}\n\nwindow() {\n\tlocal action=${1:-add}\n\tlocal iter=${2:-1}\n\tlocal delta=${3:-1}\n\tlocal event=node_${action}\n\tlocal instance_name=${INSTANCE_NAME:-test}\n\tlocal class_name=${CLASS_NAME:-Test}\n\n\tlocal cmd\n\tcase \"$action\" in\n\t\tadd) cmd=\"./test_window $instance_name $class_name\" ;;\n\t\tremove) cmd=\"bspc node -c\" ;;\n\tesac\n\twhile [ $iter -gt 0 ] ; do\n\t\tlocal rsp_chan=$(bspc subscribe -f -c \"$delta\" \"$event\")\n\t\t$cmd &\n\t\tcat \"$rsp_chan\" > /dev/null\n\t\titer=$((iter - 1))\n\tdone\n}\n"
  },
  {
    "path": "tests/run",
    "content": "#! /bin/sh\n\nfocus_follows_pointer=$(bspc config focus_follows_pointer)\ninitial_polarity=$(bspc config initial_polarity)\nautomatic_scheme=$(bspc config automatic_scheme)\nbspc config automatic_scheme spiral\nbspc config initial_polarity first_child\nbspc config focus_follows_pointer false\n\ncleanup () {\n\tbspc config automatic_scheme \"$automatic_scheme\"\n\tbspc config initial_polarity \"$initial_polarity\"\n\tbspc config focus_follows_pointer \"$focus_follows_pointer\"\n}\n\nabort() {\n\tcleanup\n\techo \"One test failed.\" 1>&2\n\texit 1\n}\n\necho \"Node\"\necho \"-> Insertion\"\n./node/insertion || abort\necho \"-> Removal\"\n./node/removal || abort\necho \"-> Transfer\"\n./node/transfer || abort\necho \"-> Swap\"\n./node/swap || abort\necho \"-> Flags\"\n./node/flags || abort\necho \"-> Receptacle\"\n./node/receptacle || abort\n\necho \"Desktop\"\necho \"-> Transfer\"\n./desktop/transfer || abort\necho \"-> Swap\"\n./desktop/swap || abort\n\ncleanup\n\necho \"All tests passed.\"\n"
  },
  {
    "path": "tests/test_window.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <xcb/xcb_event.h>\n#include <xcb/xcb_icccm.h>\n\n#define TEST_WINDOW_IC  \"test\\0Test\"\n\nbool get_atom(xcb_connection_t *dpy, char *name, xcb_atom_t *atom)\n{\n\tbool ret = true;\n\txcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);\n\tif (reply != NULL) {\n\t\t*atom = reply->atom;\n\t} else {\n\t\tret = false;\n\t}\n\tfree(reply);\n\treturn ret;\n}\n\nvoid check_request(xcb_connection_t *dpy, xcb_void_cookie_t cookie, char *msg)\n{\n\txcb_generic_error_t *err = xcb_request_check(dpy, cookie);\n\tif (err != NULL) {\n\t\tfprintf(stderr, \"%s: error code: %u.\\n\", msg, err->error_code);\n\t\txcb_disconnect(dpy);\n\t\texit(-1);\n\t}\n}\n\nxcb_gc_t get_font_gc(xcb_connection_t *dpy, xcb_window_t win, const char *font_name)\n{\n\txcb_void_cookie_t ck;\n\txcb_font_t font = xcb_generate_id(dpy);\n\tck = xcb_open_font_checked(dpy, font, strlen(font_name), font_name);\n\tcheck_request(dpy, ck, \"Can't open font\");\n\txcb_gcontext_t gc = xcb_generate_id(dpy);\n\tuint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;\n\tuint32_t values[] = {0xffcccccc, 0xff111111, font};\n\txcb_create_gc(dpy, gc, win, mask, values);\n\txcb_close_font(dpy, font);\n\treturn gc;\n}\n\nvoid render_text(xcb_connection_t *dpy, xcb_window_t win, int16_t x, int16_t y)\n{\n\tchar id[11];\n\txcb_void_cookie_t ck;\n\tsnprintf(id, sizeof(id), \"0x%08X\", win);\n\txcb_gcontext_t gc = get_font_gc(dpy, win, \"-*-fixed-medium-*-*-*-18-*-*-*-*-*-*-*\");\n\t/* Doesn't work without _checked */\n\tck = xcb_image_text_8_checked(dpy, strlen(id), win, gc, x, y, id);\n\tcheck_request(dpy, ck, \"Can't draw text\");\n\txcb_free_gc(dpy, gc);\n}\n\n\nint main(int argc, char **argv)\n{\n\tchar *wm_class = TEST_WINDOW_IC;\n\tsize_t wm_class_len = sizeof (TEST_WINDOW_IC);\n\tbool will_free_wm_class = false;\n\n\t// test instance-name class-name\n\tif (argc > 2) {\n\t\twill_free_wm_class = true;\n\t\tsize_t len1 = strlen(argv[1]);\n\t\tsize_t len2 = strlen(argv[2]);\n\t\t// 2 null terminators\n\t\twm_class_len = len1 + len2 + 2;\n\t\twm_class = malloc(wm_class_len);\n\n\t\tif (!wm_class) return 1;\n\n\t\tmemcpy(wm_class, argv[1], len1 + 1);\n\t\tmemcpy(wm_class + len1 + 1, argv[2], len2 + 1);\n\t}\n\n\txcb_connection_t *dpy = xcb_connect(NULL, NULL);\n\tif (dpy == NULL) {\n\t\tfprintf(stderr, \"Can't connect to X.\\n\");\n\t\treturn EXIT_FAILURE;\n\t}\n\txcb_atom_t WM_PROTOCOLS, WM_DELETE_WINDOW;\n\tif (!get_atom(dpy, \"WM_PROTOCOLS\", &WM_PROTOCOLS) ||\n\t    !get_atom(dpy, \"WM_DELETE_WINDOW\", &WM_DELETE_WINDOW)) {\n\t\tfprintf(stderr, \"Can't get required atoms.\\n\");\n\t\txcb_disconnect(dpy);\n\t\treturn EXIT_FAILURE;\n\t}\n\txcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;\n\tif (screen == NULL) {\n\t\tfprintf(stderr, \"Can't get current screen.\\n\");\n\t\txcb_disconnect(dpy);\n\t\treturn EXIT_FAILURE;\n\t}\n\txcb_window_t win = xcb_generate_id(dpy);\n\tuint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;\n\tuint32_t values[] = {0xff111111, XCB_EVENT_MASK_EXPOSURE};\n\txcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, screen->root, 0, 0, 320, 240, 2,\n\t                  XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);\n\txcb_icccm_set_wm_class(dpy, win, wm_class_len, wm_class);\n\txcb_map_window(dpy, win);\n\txcb_flush(dpy);\n\txcb_generic_event_t *evt;\n\tbool running = true;\n\twhile (running && (evt = xcb_wait_for_event(dpy)) != NULL) {\n\t\tuint8_t rt = XCB_EVENT_RESPONSE_TYPE(evt);\n\t\tif (rt == XCB_CLIENT_MESSAGE)  {\n\t\t\txcb_client_message_event_t *cme = (xcb_client_message_event_t *) evt;\n\t\t\tif (cme->type == WM_PROTOCOLS && cme->data.data32[0] == WM_DELETE_WINDOW) {\n\t\t\t\trunning = false;\n\t\t\t}\n\t\t} else if (rt == XCB_EXPOSE) {\n\t\t\trender_text(dpy, win, 12, 24);\n\t\t}\n\t\tfree(evt);\n\t}\n\txcb_destroy_window(dpy, win);\n\txcb_disconnect(dpy);\n\n\tif (will_free_wm_class) {\n\t\tfree(wm_class);\n\t}\n\n\treturn EXIT_SUCCESS;\n}\n"
  }
]