├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── Sourcedeps ├── VERSION ├── artworks └── bspwm_logo.svg ├── contrib ├── bash_completion ├── fish_completion ├── freedesktop │ └── bspwm.desktop └── zsh_completion ├── doc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INSTALL.md ├── MISC.md ├── TODO.md ├── bspc.1 ├── bspwm.1 └── bspwm.1.asciidoc ├── examples ├── bspwmrc ├── external_rules │ ├── bspwmrc │ └── external_rules ├── overlapping_borders │ └── bspwmrc ├── panel │ ├── bspwmrc │ ├── panel │ ├── panel_bar │ ├── panel_colors │ ├── profile │ └── sxhkdrc ├── receptacles │ ├── README.md │ ├── extract_canvas │ └── induce_rules └── sxhkdrc ├── src ├── bspc.c ├── bspwm.c ├── bspwm.h ├── common.h ├── desktop.c ├── desktop.h ├── events.c ├── events.h ├── ewmh.c ├── ewmh.h ├── geometry.c ├── geometry.h ├── helpers.c ├── helpers.h ├── history.c ├── history.h ├── jsmn.c ├── jsmn.h ├── messages.c ├── messages.h ├── monitor.c ├── monitor.h ├── parse.c ├── parse.h ├── pointer.c ├── pointer.h ├── query.c ├── query.h ├── restore.c ├── restore.h ├── rule.c ├── rule.h ├── settings.c ├── settings.h ├── stack.c ├── stack.h ├── subscribe.c ├── subscribe.h ├── tree.c ├── tree.h ├── types.h ├── window.c └── window.h └── tests ├── Makefile ├── README.md ├── desktop ├── swap └── transfer ├── node ├── flags ├── insertion ├── receptacle ├── removal ├── swap └── transfer ├── prelude ├── run └── test_window.c /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: https://editorconfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | charset = utf-8 11 | indent_style = tab 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | bspwm 3 | bspc 4 | *.o 5 | tests/test_window 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Bastien Dejean 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERCMD ?= git describe --tags 2> /dev/null 2 | VERSION := $(shell $(VERCMD) || cat VERSION) 3 | 4 | CPPFLAGS += -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" 5 | CFLAGS += -std=c99 -pedantic -Wall -Wextra -DJSMN_STRICT 6 | LDFLAGS ?= 7 | LDLIBS = $(LDFLAGS) -lm -lxcb -lxcb-util -lxcb-keysyms -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama -lxcb-shape 8 | 9 | PREFIX ?= /usr/local 10 | BINPREFIX ?= $(PREFIX)/bin 11 | MANPREFIX ?= $(PREFIX)/share/man 12 | DOCPREFIX ?= $(PREFIX)/share/doc/bspwm 13 | BASHCPL ?= $(PREFIX)/share/bash-completion/completions 14 | FISHCPL ?= $(PREFIX)/share/fish/vendor_completions.d 15 | ZSHCPL ?= $(PREFIX)/share/zsh/site-functions 16 | 17 | MD_DOCS = README.md doc/CHANGELOG.md doc/CONTRIBUTING.md doc/INSTALL.md doc/MISC.md doc/TODO.md 18 | XSESSIONS ?= $(PREFIX)/share/xsessions 19 | 20 | WM_SRC = bspwm.c helpers.c geometry.c jsmn.c settings.c monitor.c desktop.c tree.c stack.c history.c \ 21 | events.c pointer.c window.c messages.c parse.c query.c restore.c rule.c ewmh.c subscribe.c 22 | WM_OBJ := $(WM_SRC:.c=.o) 23 | CLI_SRC = bspc.c helpers.c 24 | CLI_OBJ := $(CLI_SRC:.c=.o) 25 | 26 | all: bspwm bspc 27 | 28 | debug: CFLAGS += -O0 -g 29 | debug: bspwm bspc 30 | 31 | VPATH=src 32 | 33 | include Sourcedeps 34 | 35 | $(WM_OBJ) $(CLI_OBJ): Makefile 36 | 37 | bspwm: $(WM_OBJ) 38 | 39 | bspc: $(CLI_OBJ) 40 | 41 | install: 42 | mkdir -p "$(DESTDIR)$(BINPREFIX)" 43 | cp -pf bspwm "$(DESTDIR)$(BINPREFIX)" 44 | cp -pf bspc "$(DESTDIR)$(BINPREFIX)" 45 | mkdir -p "$(DESTDIR)$(MANPREFIX)"/man1 46 | cp -p doc/bspwm.1 "$(DESTDIR)$(MANPREFIX)"/man1 47 | cp -Pp doc/bspc.1 "$(DESTDIR)$(MANPREFIX)"/man1 48 | mkdir -p "$(DESTDIR)$(BASHCPL)" 49 | cp -p contrib/bash_completion "$(DESTDIR)$(BASHCPL)"/bspc 50 | mkdir -p "$(DESTDIR)$(FISHCPL)" 51 | cp -p contrib/fish_completion "$(DESTDIR)$(FISHCPL)"/bspc.fish 52 | mkdir -p "$(DESTDIR)$(ZSHCPL)" 53 | cp -p contrib/zsh_completion "$(DESTDIR)$(ZSHCPL)"/_bspc 54 | mkdir -p "$(DESTDIR)$(DOCPREFIX)" 55 | cp -p $(MD_DOCS) "$(DESTDIR)$(DOCPREFIX)" 56 | mkdir -p "$(DESTDIR)$(DOCPREFIX)"/examples 57 | cp -pr examples/* "$(DESTDIR)$(DOCPREFIX)"/examples 58 | mkdir -p "$(DESTDIR)$(XSESSIONS)" 59 | cp -p contrib/freedesktop/bspwm.desktop "$(DESTDIR)$(XSESSIONS)" 60 | 61 | uninstall: 62 | rm -f "$(DESTDIR)$(BINPREFIX)"/bspwm 63 | rm -f "$(DESTDIR)$(BINPREFIX)"/bspc 64 | rm -f "$(DESTDIR)$(MANPREFIX)"/man1/bspwm.1 65 | rm -f "$(DESTDIR)$(MANPREFIX)"/man1/bspc.1 66 | rm -f "$(DESTDIR)$(BASHCPL)"/bspc 67 | rm -f "$(DESTDIR)$(FISHCPL)"/bspc.fish 68 | rm -f "$(DESTDIR)$(ZSHCPL)"/_bspc 69 | rm -rf "$(DESTDIR)$(DOCPREFIX)" 70 | rm -f "$(DESTDIR)$(XSESSIONS)"/bspwm.desktop 71 | 72 | doc: 73 | a2x -v -d manpage -f manpage -a revnumber=$(VERSION) doc/bspwm.1.asciidoc 74 | 75 | clean: 76 | rm -f $(WM_OBJ) $(CLI_OBJ) bspwm bspc 77 | 78 | .PHONY: all debug install uninstall doc clean 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | *bspwm* is a tiling window manager that represents windows as the leaves of a full binary tree. 4 | 5 | It only responds to X events, and the messages it receives on a dedicated socket. 6 | 7 | *bspc* is a program that writes messages on *bspwm*'s socket. 8 | 9 | *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. 10 | 11 | The outlined architecture is the following: 12 | 13 | ``` 14 | PROCESS SOCKET 15 | sxhkd --------> bspc <------> bspwm 16 | ``` 17 | 18 | ## Configuration 19 | 20 | The default configuration file is `$XDG_CONFIG_HOME/bspwm/bspwmrc`: this is simply a shell script that calls *bspc*. 21 | 22 | An argument is passed to that script to indicate whether is was executed after a restart (`$1 -gt 0`) or not (`$1 -eq 0`). 23 | 24 | Keyboard and pointer bindings are defined with [sxhkd](https://github.com/baskerville/sxhkd). 25 | 26 | Example configuration files can be found in the [examples](examples) directory. 27 | 28 | ## Monitors, desktops and windows 29 | 30 | *bspwm* holds a list of monitors. 31 | 32 | A monitor is just a rectangle that contains desktops. 33 | 34 | A desktop is just a pointer to a tree. 35 | 36 | Monitors only show the tree of one desktop at a time (their focused desktop). 37 | 38 | The tree is a partition of a monitor's rectangle into smaller rectangular regions. 39 | 40 | Each node in a tree either has zero or two children. 41 | 42 | Each internal node is responsible for splitting a rectangle in half. 43 | 44 | A split is defined by two parameters: the type (horizontal or vertical) and the ratio (a real number *r* such that *0 < r < 1*). 45 | 46 | Each leaf node holds exactly one window. 47 | 48 | ## Insertion modes 49 | 50 | When *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. 51 | 52 | The insertion mode tells *bspwm* how it should alter the tree in order to insert new windows on a given insertion point. 53 | 54 | By default the insertion point is the focused window and its insertion mode is *automatic*. 55 | 56 | ### Manual mode 57 | 58 | The 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*. 59 | 60 | The *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*). 61 | 62 | After doing so the insertion point goes into *manual* mode. 63 | 64 | Let's consider the following scenario: 65 | 66 | ``` 67 | a a a 68 | / \ / \ / \ 69 | 1 b ---> c b ---> c b 70 | ^ / \ / \ / \ / \ / \ 71 | 2 3 4 1 2 3 d 1 2 3 72 | ^ / \ 73 | 5 4 74 | ^ 75 | 76 | +-----------------------+ +-----------------------+ +-----------------------+ 77 | | | | | | | | | | | 78 | | | 2 | | 4 | 2 | | 5 | 4 | 2 | 79 | | | | | ^ | | | ^ | | | 80 | | 1 |-----------| |-----------|-----------| |-----------|-----------| 81 | | ^ | | | | | | | | 82 | | | 3 | | 1 | 3 | | 1 | 3 | 83 | | | | | | | | | | 84 | +-----------------------+ +-----------------------+ +-----------------------+ 85 | 86 | X Y Z 87 | ``` 88 | 89 | In state *X*, the insertion point is *1*. 90 | 91 | We send the following message to *bspwm*: *node -p north*. 92 | 93 | Then add a new window: *4*, this leads to state *Y*: the new internal node, *c* becomes *a*'s first child. 94 | 95 | Finally we send another message: *node -p west* and add window *5*. 96 | 97 | The 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. 98 | 99 | ### Automatic mode 100 | 101 | The *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. 102 | 103 | #### Longest side scheme 104 | 105 | When 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. 106 | 107 | Let's consider the following scenario, where the initial polarity is set to `second_child`: 108 | 109 | ``` 110 | 1 a a 111 | ^ / \ / \ 112 | ---> 1 2 ---> 1 b 113 | ^ / \ 114 | 2 3 115 | ^ 116 | 117 | +-----------------------+ +-----------------------+ +-----------------------+ 118 | | | | | | | | | 119 | | | | | | | | 2 | 120 | | | | | | | | | 121 | | 1 | | 1 | 2 | | 1 |-----------| 122 | | ^ | | | ^ | | | | 123 | | | | | | | | 3 | 124 | | | | | | | | ^ | 125 | +-----------------------+ +-----------------------+ +-----------------------+ 126 | 127 | X Y Z 128 | ``` 129 | 130 | In state *X*, a new window is added. 131 | 132 | Since *1* is wide, it gets split vertically and *2* is added as *a*'s second child given the initial polarity. 133 | 134 | This 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. 135 | 136 | #### Alternate scheme 137 | 138 | When 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. 139 | 140 | #### Spiral scheme 141 | 142 | When the value of the automatic scheme is `spiral`, the window will *take the space* of the insertion point. 143 | 144 | Let's dive into the details with the following scenario: 145 | 146 | ``` 147 | a a a 148 | / \ / \ / \ 149 | 1 b ---> 1 c ---> 1 d 150 | / \ / \ / \ 151 | 2 3 4 b 5 c 152 | ^ ^ / \ ^ / \ 153 | 3 2 b 4 154 | / \ 155 | 3 2 156 | 157 | +-----------------------+ +-----------------------+ +-----------------------+ 158 | | | | | | | | | | 159 | | | 2 | | | 4 | | | 5 | 160 | | | ^ | | | ^ | | | ^ | 161 | | 1 |-----------| | 1 |-----------| | 1 |-----------| 162 | | | | | | | | | | 3 | | 163 | | | 3 | | | 3 | 2 | | |-----| 4 | 164 | | | | | | | | | | 2 | | 165 | +-----------------------+ +-----------------------+ +-----------------------+ 166 | 167 | X Y Z 168 | ``` 169 | 170 | In state *X*, the insertion point, *2* is in automatic mode. 171 | 172 | When we add a new window, *4*, the whole tree rooted at *b* is reattached, as the second child of a new internal node, *c*. 173 | 174 | The splitting parameters of *b* (type: *horizontal*, ratio: *½*) are copied to *c* and *b* is rotated by 90° clockwise. 175 | 176 | The tiling rectangle of *4* in state *Y* is equal to the tiling rectangle of *2* in state *X*. 177 | 178 | Then the insertion of *5*, with *4* as insertion point, leads to *Z*. 179 | 180 | The *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. 181 | 182 | 183 | ## Supported protocols and standards 184 | 185 | - The RandR and Xinerama protocols. 186 | - A subset of the EWMH and ICCCM standards. 187 | 188 | ## Community 189 | 190 | Want to get in touch with other *bspwm* users or you need help? Join us on our: 191 | 192 | - Subreddit at [r/bspwm](https://www.reddit.com/r/bspwm/). 193 | - IRC channel at `#bspwm` on `irc.libera.chat` (maintained by [Emanuele Torre](https://github.com/emanuele6) / emanuele6 on IRC). 194 | - Matrix room at https://matrix.to/#/#bspwm:matrix.org 195 | -------------------------------------------------------------------------------- /Sourcedeps: -------------------------------------------------------------------------------- 1 | bspc.o: bspc.c common.h helpers.h 2 | bspwm.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 3 | desktop.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 4 | events.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 5 | ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h 6 | geometry.o: geometry.c geometry.h helpers.h types.h 7 | helpers.o: helpers.c bspwm.h helpers.h types.h 8 | history.o: history.c bspwm.h helpers.h query.h tree.h types.h 9 | jsmn.o: jsmn.c jsmn.h 10 | messages.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 11 | monitor.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 12 | parse.o: parse.c helpers.h parse.h subscribe.h types.h 13 | pointer.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 14 | query.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 15 | restore.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 16 | rule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h subscribe.h types.h window.h 17 | settings.o: settings.c bspwm.h helpers.h settings.h types.h 18 | stack.o: stack.c bspwm.h ewmh.h helpers.h stack.h subscribe.h tree.h types.h window.h 19 | subscribe.o: subscribe.c bspwm.h desktop.h helpers.h settings.h subscribe.h types.h 20 | tree.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 21 | window.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 22 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.9.10 -------------------------------------------------------------------------------- /artworks/bspwm_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contrib/bash_completion: -------------------------------------------------------------------------------- 1 | _bspc() { 2 | local commands='node desktop monitor query rule wm subscribe config quit' 3 | 4 | local 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' 5 | 6 | COMPREPLY=() 7 | 8 | if [[ $COMP_CWORD -ge 1 ]] ; then 9 | local current_word="${COMP_WORDS[COMP_CWORD]}" 10 | if [[ $COMP_CWORD -eq 1 ]] ; then 11 | COMPREPLY=( $(compgen -W "$commands" -- "$current_word") ) 12 | return 0 13 | else 14 | local second_word=${COMP_WORDS[1]} 15 | case $second_word in 16 | config) 17 | if [[ $COMP_CWORD -eq 2 ]] ; then 18 | COMPREPLY=( $(compgen -W "$settings" -- "$current_word") ) 19 | return 0 20 | fi 21 | ;; 22 | esac 23 | fi 24 | fi 25 | } 26 | 27 | complete -F _bspc bspc 28 | 29 | # vim: set ft=sh: 30 | -------------------------------------------------------------------------------- /contrib/fish_completion: -------------------------------------------------------------------------------- 1 | function __fish_bspc_needs_command 2 | set cmd (commandline -opc) 3 | [ (count $cmd) -eq 1 -a $cmd[1] = 'bspc' ]; and return 0 4 | return 1 5 | end 6 | 7 | function __fish_bspc_using_command 8 | set cmd (commandline -opc) 9 | [ (count $cmd) -gt 1 ]; and [ $argv[1] = $cmd[2] ]; and return 0 10 | return 1 11 | end 12 | 13 | complete -f -c bspc -n '__fish_bspc_needs_command' -a 'node desktop monitor query rule wm subscribe config quit' 14 | complete -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' 15 | -------------------------------------------------------------------------------- /contrib/freedesktop/bspwm.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=bspwm 3 | Comment=Binary space partitioning window manager 4 | Exec=bspwm 5 | Type=Application 6 | -------------------------------------------------------------------------------- /doc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # From 0.9.9 to 0.9.10 2 | 3 | ## Additions 4 | 5 | - New node descriptor: `first_ancestor`. 6 | - New node modifiers: `horizontal`, `vertical`. 7 | 8 | ## Changes 9 | 10 | - The node descriptors `next` and `prev` might now return any node. The previous behavior can be emulated by appending `.!hidden.window`. 11 | - 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`. 12 | - The *query* command now handles all the possible descriptor-free constraints (for example, `query -N -d .active` now works as expected). 13 | - The rules can now match against the window's names (`WM_NAME`). 14 | - The configuration script now receives an argument to indicate whether is was executed after a restart or not. 15 | - The *intermediate consequences* passed to the external rules command are now in resolved form to avoid unwanted code execution. 16 | 17 | # From 0.9.8 to 0.9.9 18 | 19 | - Fix a memory allocation bug in the implementation of `wm --restart`. 20 | - Honor `single_monocle` when the `hidden` flag is toggled. 21 | 22 | # From 0.9.7 to 0.9.8 23 | 24 | - Fix a potential infinite loop. 25 | - Fix two bugs having to do with `single_monocle`. 26 | - Honor `removal_adjustment` for the spiral automatic insertion scheme. 27 | 28 | # From 0.9.6 to 0.9.7 29 | 30 | This release fixes a bug in the behavior of `single_monocle`. 31 | 32 | # From 0.9.4 to 0.9.6 33 | 34 | ## Additions 35 | 36 | - 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. 37 | - 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. 38 | - New settings: `ignore_ewmh_struts`, `presel_feedback`, `{top,right,bottom,left}_monocle_padding`. 39 | - New node descriptor: `smallest`. 40 | - New desktop modifier: `active`. 41 | 42 | ## Changes 43 | 44 | - The `focused` and `active` modifiers now mean the same thing across every object. 45 | - 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. 46 | - Pseudo-tiled windows now shrink automatically. 47 | 48 | ## Removals 49 | 50 | - The `paddingless_monocle` setting was removed (and subsumed). The effect of `paddingless_monocle` can now be achieved with: 51 | ```shell 52 | for side in top right bottom left; do 53 | bspc config ${side}_monocle_padding -$(bspc config ${side}_padding) 54 | done 55 | ``` 56 | 57 | # From 0.9.3 to 0.9.4 58 | 59 | ## Changes 60 | 61 | - The following events: `node_{manage,unmanage}` are now `node_{add,remove}`. 62 | 63 | ## Additions 64 | 65 | - New monitor/desktop/node descriptors: `any`, `newest`. 66 | - New node flag: `marked`. 67 | - New monitor descriptor: `pointed`. 68 | - New *wm* command: `--reorder-monitors`. 69 | - Receptacles are now described in the manual. 70 | - New `--follow` option added to `node -{m,d,n,s}` and `desktop -{m,s}`. 71 | - The *subscribe* command now has the following options: `--fifo`, `--count`. 72 | - New settings: `ignore_ewmh_fullscreen`, `mapping_events_count`. 73 | 74 | # From 0.9.2 to 0.9.3 75 | 76 | ## Changes 77 | 78 | - *click_to_focus* is now a button name. Specifying a boolean is deprecated but will still work (`true` is equivalent to `button1`). 79 | 80 | ## Additions 81 | 82 | - `node -r` now accepts a relative fraction argument. 83 | - An option was added to `query -{M,D,N}` in order to output names instead of IDs: `--names`. 84 | - New rule consequence: `rectangle=WxH+X+Y`. 85 | - New settings: `swallow_first_click` and `directional_focus_tightness`. 86 | 87 | # From 0.9.1 to 0.9.2 88 | 89 | ## Changes 90 | 91 | - Monitors, desktops and nodes have unique IDs, `bspc query -{N,D,M}` returns IDs and events reference objects by ID instead of name. 92 | - `bspc` fails verbosely and only returns a single non-zero exit code. 93 | - The `DIR` descriptor is based on [right-window](https://github.com/ntrrgc/right-window). 94 | - 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.) 95 | - `bspc query -{N,D,M}` accepts an optional reference argument used by certain descriptors/modifiers. 96 | - Monitors are ordered visually by default. 97 | - The following settings: `border_width`, `window_gap` and `*_padding` behave as expected. 98 | - External rules also receives the monitor, desktop and node selectors computed from the built-in rules stage as subsequent arguments. 99 | - The `focus_follows_pointer` setting is implemented via enter notify events. 100 | 101 | ## Additions 102 | 103 | - Nodes can be hidden/shown via the new `hidden` flag. 104 | - Node receptacles can be inserted with `node -i`. An example is given in `git show e8aa679`. 105 | - Non-tiled nodes can be moved/resized via `node -{v,z}`. 106 | - 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`. 107 | - Node descriptors: ``, `pointed`. 108 | - Node modifiers: `hidden`, `descendant_of`, `ancestor_of`, `window`, `active`. Example: `bspc query -N 0x00400006 -n .descendant_of` returns the descendants of `0x00400006`. 109 | - Desktop descriptor: ``. 110 | - Monitor descriptor: ``. 111 | - Settings: `pointer_motion_interval`, `pointer_modifier`, `pointer_action{1,2,3}`, `click_to_focus`, `honor_size_hints`. 112 | - Event: `pointer_action`. 113 | - ICCCM/EWMH atoms: `WM_STATE`, `_NET_WM_STRUT_PARTIAL`. 114 | - `bspc` shell completions for `fish`. 115 | 116 | ## Removals 117 | 118 | - The `pointer` domain. Pointer actions are handled internally. You need to remove any binding that uses this domain from your `sxhkdrc`. 119 | - Settings: `history_aware_focus`, `focus_by_distance`. Both settings are merged into the new `DIR` implementation. 120 | - `monitor -r|--remove-desktops`: use `desktop -r|--remove` instead. 121 | - `wm -r|--remove-monitor`: use `monitor -r|--remove` instead. 122 | 123 | # From 0.9 to 0.9.1 124 | 125 | ## Overview 126 | 127 | All 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. 128 | 129 | ## Changes 130 | 131 | - All the commands that started with `window` now start with `node`. 132 | - `-W|--windows`, `-w|--window`, `-w|--to-window` are now `-N|--nodes`, `-n|--node`, `-n|--to-node`. 133 | - 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). 134 | - The `WINDOW_SEL` becomes `NODE_SEL` and now contains a `PATH` specifier to select internal nodes. 135 | - The `control` domain is renamed to `wm`. 136 | - `restore -{T,H,S}` was unified into `wm -l|--load-state` and `query -{T,H,S}` into `wm -d|--dump-state`. 137 | - `control --subscribe` becomes `subscribe`. 138 | - `node --toggle` (previously `window --toggle`) is split into `node --state` and `node --flag`. 139 | - The preselection direction (resp. ratio) is now set with `node --presel-dir|-p` (resp. `node --presel-ratio|-o`). 140 | - The following desktop commands: `--rotate`, `--flip`, `--balance`, `--equalize`, `--circulate` are now node commands. 141 | - `query -T ...` outputs JSON. 142 | - `query -{M,D,N}`: the descriptor part of the selector is now optional (e.g.: `query -D -d .urgent`). 143 | - 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.). 144 | - Modifiers can now be applied to any descriptor (e.g.: `query -N -n 0x80000d.floating`). 145 | - `wm -l` (previously `restore -T`) will now destroy the existing tree and restore from scratch instead of relying on existing monitors and desktops. 146 | - `subscribe` (previously `control --subscribe`) now accepts arguments and can receive numerous events from different domains (see the *EVENTS* section of the manual). 147 | - `rule -a`: it is now possible to specify the class name *and* instance name (e.g.: `rule -a Foo:bar`). 148 | - `presel_border_color` is now `presel_feedback_color`. 149 | - `bspwm -v` yields an accurate version. 150 | - The monitors are sorted, by default, according to the natural visual hierarchy. 151 | 152 | ## Additions 153 | 154 | ### Settings 155 | 156 | - `single_monocle`. 157 | - `paddingless_monocle`. 158 | 159 | ### Commands 160 | 161 | - `{node,desktop} --activate`. 162 | - `node --layer`. 163 | - `desktop --bubble`. 164 | - `wm {--add-monitor,--remove-monitor}`. 165 | - `monitor --rectangle`. 166 | 167 | ## Removals 168 | 169 | ### Commands 170 | 171 | - `desktop --toggle` 172 | - `desktop --cancel-presel` 173 | - `control --toggle-visibility`. 174 | 175 | ### Settings 176 | 177 | - `apply_floating_atom`. 178 | - `auto_alternate`. 179 | - `auto_cancel`. 180 | - `focused_locked_border_color` 181 | - `active_locked_border_color` 182 | - `normal_locked_border_color` 183 | - `focused_sticky_border_color` 184 | - `active_sticky_border_color` 185 | - `normal_sticky_border_color` 186 | - `focused_private_border_color` 187 | - `active_private_border_color` 188 | - `normal_private_border_color` 189 | - `urgent_border_color` 190 | 191 | ## Message Translation Guide 192 | 193 | 0.9 | 0.9.1 194 | -----------------------------------------|---------------------------------- 195 | `{left,down,up,right}` | `{west,south,north,east}` 196 | `window -r` | `node -o` (`node -r` also exists) 197 | `window -e DIR RATIO` | `node @DIR -r RATIO` 198 | `window -R DIR DEG` | `node @DIR -R DEG` 199 | `window -w` | `node -n` 200 | `desktop DESKTOP_SEL -R DEG` | `node @DESKTOP_SEL:/ -R DEG` 201 | `desktop DESKTOP_SEL -E` | `node @DESKTOP_SEL:/ -E` 202 | `desktop DESKTOP_SEL -B` | `node @DESKTOP_SEL:/ -B` 203 | `desktop DESKTOP_SEL -C forward|backward`| `node @DESKTOP_SEL:/ -C forward|backward` 204 | `desktop DESKTOP_SEL --cancel-presel` | `bspc query -N -d DESKTOP_SEL | xargs -I id -n 1 bspc node id -p cancel` 205 | `window -t floating` | `node -t ~floating` 206 | `query -W -w` | `query -N -n .leaf` 207 | `query -{T,H,S}` | `wm -d` 208 | `restore -{T,H,S}` | `wm -l` 209 | -------------------------------------------------------------------------------- /doc/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Issues 2 | 3 | Always provide the following information when submitting an issue: 4 | - Output of `bspwm -v`. 5 | - Content of `bspwmrc`. 6 | - Steps to reproduce the problem. 7 | 8 | ## Pull Requests 9 | 10 | ### Requirements 11 | 12 | You must be comfortable with [C][1], [XCB][2] and [Git][3]. 13 | 14 | ### Coding Style 15 | 16 | I follow the [Linux Coding Style][4] with the following variations: 17 | - [Indent with tabs, align with spaces][5]. 18 | - Always use braces when using control structures. 19 | 20 | An [EditorConfig][6] is included for convinience. 21 | 22 | [1]: https://www.bell-labs.com/usr/dmr/www/cbook/ 23 | [2]: https://xcb.freedesktop.org/tutorial/ 24 | [3]: http://git-scm.com/documentation 25 | [4]: https://www.kernel.org/doc/Documentation/process/coding-style.rst 26 | [5]: http://lea.verou.me/2012/01/why-tabs-are-clearly-superior/ 27 | [6]: https://editorconfig.org 28 | 29 | ## Donations 30 | 31 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RHTYMMB9SHP68) 32 | -------------------------------------------------------------------------------- /doc/INSTALL.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | 3 | - libxcb 4 | - xcb-util 5 | - xcb-util-wm 6 | 7 | # Installation 8 | 9 | $ make 10 | # make install 11 | 12 | # Removal 13 | 14 | # make uninstall 15 | 16 | # Packages 17 | 18 | - Arch Linux 19 | - [bspwm-git](https://aur.archlinux.org/packages/bspwm-git) 20 | - [bspwm (x86_64)](https://www.archlinux.org/packages/community/x86_64/bspwm) 21 | - [bspwm (i686)](https://www.archlinux.org/packages/community/i686/bspwm) 22 | 23 | - Debian 24 | - [bspwm](https://tracker.debian.org/pkg/bspwm) 25 | - [wiki page](https://wiki.debian.org/bspwm) 26 | 27 | - Gentoo Linux 28 | - [bspwm](https://packages.gentoo.org/packages/x11-wm/bspwm) 29 | - [bspwm-git](https://github.com/milomouse/ebuilds) 30 | 31 | - [FreeBSD](https://www.freshports.org/x11-wm/bspwm) 32 | 33 | - [Void Linux](https://github.com/voidlinux/documentation/wiki/bspwm) 34 | 35 | - [BunsenLabs](https://forums.bunsenlabs.org/viewtopic.php?id=567) 36 | 37 | - [Manjaro Linux](https://forum.manjaro.org/index.php?topic=16994.0) 38 | 39 | - [Chakra](https://chakraos.org/ccr/packages.php?ID=6537) 40 | 41 | - [Exherbo](http://git.exherbo.org/summer/packages/x11-wm/bspwm) 42 | -------------------------------------------------------------------------------- /doc/MISC.md: -------------------------------------------------------------------------------- 1 | # Mathematical background 2 | 3 | The main data structure is a full binary tree. 4 | 5 | A binary tree is *full* if each of its node has either two or zero children. 6 | 7 | If a node has two children it is an internal node, otherwise a leaf. 8 | 9 | Fundamental theorem: 10 | Let I be the number of internal nodes and L the number of leaves, then: 11 | L = I + 1 12 | 13 | (It can be proved by induction on the number of internal nodes.) 14 | 15 | This means that when we add a leaf to the tree (when a window is created), we must also add one internal node. 16 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | - Add zoom feature (view point distinct from root). 2 | - Use BSD `sys/{queue/tree}.h` for {list,tree} structures? 3 | -------------------------------------------------------------------------------- /doc/bspc.1: -------------------------------------------------------------------------------- 1 | bspwm.1 -------------------------------------------------------------------------------- /examples/bspwmrc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | pgrep -x sxhkd > /dev/null || sxhkd & 4 | 5 | bspc monitor -d I II III IV V VI VII VIII IX X 6 | 7 | bspc config border_width 2 8 | bspc config window_gap 12 9 | 10 | bspc config split_ratio 0.52 11 | bspc config borderless_monocle true 12 | bspc config gapless_monocle true 13 | 14 | bspc rule -a Gimp desktop='^8' state=floating follow=on 15 | bspc rule -a Chromium desktop='^2' 16 | bspc rule -a mplayer2 state=floating 17 | bspc rule -a Kupfer.py focus=on 18 | bspc rule -a Screenkey manage=off 19 | -------------------------------------------------------------------------------- /examples/external_rules/bspwmrc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | bspc config external_rules_command "$(which external_rules)" 4 | -------------------------------------------------------------------------------- /examples/external_rules/external_rules: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | wid=$1 4 | class=$2 5 | instance=$3 6 | consequences=$4 7 | 8 | if [ "$instance" = fontforge ] ; then 9 | title=$(xtitle "$wid") 10 | case "$title" in 11 | Layers|Tools|Warning) 12 | echo "focus=off" 13 | ;; 14 | esac 15 | fi 16 | 17 | case "$class" in 18 | Lutris|Liferea) 19 | eval "$consequences" 20 | [ "$state" ] || echo "state=pseudo_tiled" 21 | ;; 22 | esac 23 | -------------------------------------------------------------------------------- /examples/overlapping_borders/bspwmrc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | BW=3 4 | bspc config border_width $BW 5 | bspc config window_gap -$BW 6 | for side in top right bottom left ; do 7 | bspc config ${side}_padding $BW 8 | done 9 | -------------------------------------------------------------------------------- /examples/panel/bspwmrc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | pgrep -x panel > /dev/null || panel & 4 | -------------------------------------------------------------------------------- /examples/panel/panel: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | if xdo id -a "$PANEL_WM_NAME" > /dev/null ; then 4 | printf "%s\n" "The panel is already running." >&2 5 | exit 1 6 | fi 7 | 8 | trap 'trap - TERM; kill 0' INT TERM QUIT EXIT 9 | 10 | [ -e "$PANEL_FIFO" ] && rm "$PANEL_FIFO" 11 | mkfifo "$PANEL_FIFO" 12 | 13 | xtitle -sf 'T%s\n' > "$PANEL_FIFO" & 14 | clock -sf 'S%a %H:%M' > "$PANEL_FIFO" & 15 | bspc subscribe report > "$PANEL_FIFO" & 16 | 17 | . panel_colors 18 | 19 | panel_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 & 20 | 21 | wid=$(xdo id -m -a "$PANEL_WM_NAME") 22 | xdo above -t "$(xdo id -N Bspwm -n root | sort | head -n 1)" "$wid" 23 | 24 | wait 25 | -------------------------------------------------------------------------------- /examples/panel/panel_bar: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Example panel for lemonbar 4 | 5 | . panel_colors 6 | 7 | num_mon=$(bspc query -M | wc -l) 8 | 9 | while read -r line ; do 10 | case $line in 11 | S*) 12 | # clock output 13 | sys="%{F$COLOR_SYS_FG}%{B$COLOR_SYS_BG} ${line#?} %{B-}%{F-}" 14 | ;; 15 | T*) 16 | # xtitle output 17 | title="%{F$COLOR_TITLE_FG}%{B$COLOR_TITLE_BG} ${line#?} %{B-}%{F-}" 18 | ;; 19 | W*) 20 | # bspwm's state 21 | wm= 22 | IFS=':' 23 | set -- ${line#?} 24 | while [ $# -gt 0 ] ; do 25 | item=$1 26 | name=${item#?} 27 | case $item in 28 | [mM]*) 29 | case $item in 30 | m*) 31 | # monitor 32 | FG=$COLOR_MONITOR_FG 33 | BG=$COLOR_MONITOR_BG 34 | on_focused_monitor= 35 | ;; 36 | M*) 37 | # focused monitor 38 | FG=$COLOR_FOCUSED_MONITOR_FG 39 | BG=$COLOR_FOCUSED_MONITOR_BG 40 | on_focused_monitor=1 41 | ;; 42 | esac 43 | [ $num_mon -lt 2 ] && shift && continue 44 | wm="${wm}%{F${FG}}%{B${BG}}%{A:bspc monitor -f ${name}:} ${name} %{A}%{B-}%{F-}" 45 | ;; 46 | [fFoOuU]*) 47 | case $item in 48 | f*) 49 | # free desktop 50 | FG=$COLOR_FREE_FG 51 | BG=$COLOR_FREE_BG 52 | UL=$BG 53 | ;; 54 | F*) 55 | if [ "$on_focused_monitor" ] ; then 56 | # focused free desktop 57 | FG=$COLOR_FOCUSED_FREE_FG 58 | BG=$COLOR_FOCUSED_FREE_BG 59 | UL=$BG 60 | else 61 | # active free desktop 62 | FG=$COLOR_FREE_FG 63 | BG=$COLOR_FREE_BG 64 | UL=$COLOR_FOCUSED_FREE_BG 65 | fi 66 | ;; 67 | o*) 68 | # occupied desktop 69 | FG=$COLOR_OCCUPIED_FG 70 | BG=$COLOR_OCCUPIED_BG 71 | UL=$BG 72 | ;; 73 | O*) 74 | if [ "$on_focused_monitor" ] ; then 75 | # focused occupied desktop 76 | FG=$COLOR_FOCUSED_OCCUPIED_FG 77 | BG=$COLOR_FOCUSED_OCCUPIED_BG 78 | UL=$BG 79 | else 80 | # active occupied desktop 81 | FG=$COLOR_OCCUPIED_FG 82 | BG=$COLOR_OCCUPIED_BG 83 | UL=$COLOR_FOCUSED_OCCUPIED_BG 84 | fi 85 | ;; 86 | u*) 87 | # urgent desktop 88 | FG=$COLOR_URGENT_FG 89 | BG=$COLOR_URGENT_BG 90 | UL=$BG 91 | ;; 92 | U*) 93 | if [ "$on_focused_monitor" ] ; then 94 | # focused urgent desktop 95 | FG=$COLOR_FOCUSED_URGENT_FG 96 | BG=$COLOR_FOCUSED_URGENT_BG 97 | UL=$BG 98 | else 99 | # active urgent desktop 100 | FG=$COLOR_URGENT_FG 101 | BG=$COLOR_URGENT_BG 102 | UL=$COLOR_FOCUSED_URGENT_BG 103 | fi 104 | ;; 105 | esac 106 | wm="${wm}%{F${FG}}%{B${BG}}%{U${UL}}%{+u}%{A:bspc desktop -f ${name}:} ${name} %{A}%{B-}%{F-}%{-u}" 107 | ;; 108 | [LTG]*) 109 | # layout, state and flags 110 | wm="${wm}%{F$COLOR_STATE_FG}%{B$COLOR_STATE_BG} ${name} %{B-}%{F-}" 111 | ;; 112 | esac 113 | shift 114 | done 115 | ;; 116 | esac 117 | printf "%s\n" "%{l}${wm}%{c}${title}%{r}${sys}" 118 | done 119 | -------------------------------------------------------------------------------- /examples/panel/panel_colors: -------------------------------------------------------------------------------- 1 | COLOR_DEFAULT_FG="#a7a5a5" 2 | COLOR_DEFAULT_BG="#333232" 3 | COLOR_MONITOR_FG="#8dbcdf" 4 | COLOR_MONITOR_BG="#333232" 5 | COLOR_FOCUSED_MONITOR_FG="#b1d0e8" 6 | COLOR_FOCUSED_MONITOR_BG="#144b6c" 7 | COLOR_FREE_FG="#737171" 8 | COLOR_FREE_BG="#333232" 9 | COLOR_FOCUSED_FREE_FG="#000000" 10 | COLOR_FOCUSED_FREE_BG="#504e4e" 11 | COLOR_OCCUPIED_FG="#a7a5a5" 12 | COLOR_OCCUPIED_BG="#333232" 13 | COLOR_FOCUSED_OCCUPIED_FG="#d6d3d2" 14 | COLOR_FOCUSED_OCCUPIED_BG="#504e4e" 15 | COLOR_URGENT_FG="#f15d66" 16 | COLOR_URGENT_BG="#333232" 17 | COLOR_FOCUSED_URGENT_FG="#501d1f" 18 | COLOR_FOCUSED_URGENT_BG="#d5443e" 19 | COLOR_STATE_FG="#89b09c" 20 | COLOR_STATE_BG="#333232" 21 | COLOR_TITLE_FG="#a8a2c0" 22 | COLOR_TITLE_BG="#333232" 23 | COLOR_SYS_FG="#b1a57d" 24 | COLOR_SYS_BG="#333232" 25 | -------------------------------------------------------------------------------- /examples/panel/profile: -------------------------------------------------------------------------------- 1 | PANEL_FIFO=/tmp/panel-fifo 2 | PANEL_HEIGHT=24 3 | PANEL_FONT="-*-fixed-*-*-*-*-10-*-*-*-*-*-*-*" 4 | PANEL_WM_NAME=bspwm_panel 5 | export PANEL_FIFO PANEL_HEIGHT PANEL_FONT PANEL_WM_NAME 6 | -------------------------------------------------------------------------------- /examples/panel/sxhkdrc: -------------------------------------------------------------------------------- 1 | super + alt + Escape 2 | pkill -x panel; bspc quit 3 | -------------------------------------------------------------------------------- /examples/receptacles/README.md: -------------------------------------------------------------------------------- 1 | The scripts present in this directory can be used to store and recreate layouts. 2 | 3 | Both scripts take a JSON state (output of `bspc wm -d`) as input or argument. 4 | 5 | - `extract_canvas [state.json]` outputs a new JSON state where each leaf window is replaced by a receptacle. 6 | 7 | - `induce_rules [state.json]` outputs a list of commands that, if executed, will create rules to place each window in the corresponding receptacle. 8 | -------------------------------------------------------------------------------- /examples/receptacles/extract_canvas: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | import json 5 | 6 | source = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin 7 | state = json.load(source) 8 | 9 | def nullify_clients(node): 10 | if node is None: 11 | return 12 | elif node['client'] is None: 13 | nullify_clients(node['firstChild']) 14 | nullify_clients(node['secondChild']) 15 | else: 16 | node['client'] = None 17 | 18 | state['clientsCount'] = 0 19 | state['focusHistory'] = [] 20 | state['stackingList'] = [] 21 | 22 | for monitor in state['monitors']: 23 | for desktop in monitor['desktops']: 24 | desktop['focusedNodeId'] = 0 25 | nullify_clients(desktop['root']) 26 | 27 | print(json.dumps(state)) 28 | -------------------------------------------------------------------------------- /examples/receptacles/induce_rules: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | import json 5 | 6 | source = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin 7 | state = json.load(source) 8 | 9 | def print_rules(prefix, node, path): 10 | if node is None: 11 | return 12 | elif node['client'] is None: 13 | print_rules(prefix, node['firstChild'], path+['1']) 14 | print_rules(prefix, node['secondChild'], path+['2']) 15 | else: 16 | client = node['client'] 17 | print('bspc rule -a {}:{} -o node={}{}'.format(client['className'], 18 | client['instanceName'], 19 | prefix, '/'.join(path))) 20 | 21 | for i, monitor in enumerate(state['monitors']): 22 | for j, desktop in enumerate(monitor['desktops']): 23 | print_rules('@^{}:^{}:/'.format(i+1, j+1), desktop['root'], []) 24 | -------------------------------------------------------------------------------- /examples/sxhkdrc: -------------------------------------------------------------------------------- 1 | # 2 | # wm independent hotkeys 3 | # 4 | 5 | # terminal emulator 6 | super + Return 7 | urxvt 8 | 9 | # program launcher 10 | super + @space 11 | dmenu_run 12 | 13 | # make sxhkd reload its configuration files: 14 | super + Escape 15 | pkill -USR1 -x sxhkd 16 | 17 | # 18 | # bspwm hotkeys 19 | # 20 | 21 | # quit/restart bspwm 22 | super + alt + {q,r} 23 | bspc {quit,wm -r} 24 | 25 | # close and kill 26 | super + {_,shift + }w 27 | bspc node -{c,k} 28 | 29 | # alternate between the tiled and monocle layout 30 | super + m 31 | bspc desktop -l next 32 | 33 | # send the newest marked node to the newest preselected node 34 | super + y 35 | bspc node newest.marked.local -n newest.!automatic.local 36 | 37 | # swap the current node and the biggest window 38 | super + g 39 | bspc node -s biggest.window 40 | 41 | # 42 | # state/flags 43 | # 44 | 45 | # set the window state 46 | super + {t,shift + t,s,f} 47 | bspc node -t {tiled,pseudo_tiled,floating,fullscreen} 48 | 49 | # set the node flags 50 | super + ctrl + {m,x,y,z} 51 | bspc node -g {marked,locked,sticky,private} 52 | 53 | # 54 | # focus/swap 55 | # 56 | 57 | # focus the node in the given direction 58 | super + {_,shift + }{h,j,k,l} 59 | bspc node -{f,s} {west,south,north,east} 60 | 61 | # focus the node for the given path jump 62 | super + {p,b,comma,period} 63 | bspc node -f @{parent,brother,first,second} 64 | 65 | # focus the next/previous window in the current desktop 66 | super + {_,shift + }c 67 | bspc node -f {next,prev}.local.!hidden.window 68 | 69 | # focus the next/previous desktop in the current monitor 70 | super + bracket{left,right} 71 | bspc desktop -f {prev,next}.local 72 | 73 | # focus the last node/desktop 74 | super + {grave,Tab} 75 | bspc {node,desktop} -f last 76 | 77 | # focus the older or newer node in the focus history 78 | super + {o,i} 79 | bspc wm -h off; \ 80 | bspc node {older,newer} -f; \ 81 | bspc wm -h on 82 | 83 | # focus or send to the given desktop 84 | super + {_,shift + }{1-9,0} 85 | bspc {desktop -f,node -d} '^{1-9,10}' 86 | 87 | # 88 | # preselect 89 | # 90 | 91 | # preselect the direction 92 | super + ctrl + {h,j,k,l} 93 | bspc node -p {west,south,north,east} 94 | 95 | # preselect the ratio 96 | super + ctrl + {1-9} 97 | bspc node -o 0.{1-9} 98 | 99 | # cancel the preselection for the focused node 100 | super + ctrl + space 101 | bspc node -p cancel 102 | 103 | # cancel the preselection for the focused desktop 104 | super + ctrl + shift + space 105 | bspc query -N -d | xargs -I id -n 1 bspc node id -p cancel 106 | 107 | # 108 | # move/resize 109 | # 110 | 111 | # expand a window by moving one of its side outward 112 | super + alt + {h,j,k,l} 113 | bspc node -z {left -20 0,bottom 0 20,top 0 -20,right 20 0} 114 | 115 | # contract a window by moving one of its side inward 116 | super + alt + shift + {h,j,k,l} 117 | bspc node -z {right -20 0,top 0 20,bottom 0 -20,left 20 0} 118 | 119 | # move a floating window 120 | super + {Left,Down,Up,Right} 121 | bspc node -v {-20 0,0 20,0 -20,20 0} 122 | -------------------------------------------------------------------------------- /src/bspc.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "helpers.h" 35 | #include "common.h" 36 | 37 | int main(int argc, char *argv[]) 38 | { 39 | int sock_fd; 40 | struct sockaddr_un sock_address; 41 | char msg[BUFSIZ], rsp[BUFSIZ]; 42 | 43 | if (argc < 2) { 44 | err("No arguments given.\n"); 45 | } 46 | 47 | sock_address.sun_family = AF_UNIX; 48 | char *sp; 49 | 50 | sp = getenv(SOCKET_ENV_VAR); 51 | if (sp != NULL) { 52 | snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp); 53 | } else { 54 | char *host = NULL; 55 | int dn = 0, sn = 0; 56 | if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) { 57 | snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn); 58 | } 59 | free(host); 60 | } 61 | 62 | if (streq(argv[1], "--print-socket-path")) { 63 | printf("%s\n", sock_address.sun_path); 64 | return EXIT_SUCCESS; 65 | } 66 | 67 | if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 68 | err("Failed to create the socket.\n"); 69 | } 70 | 71 | if (connect(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) { 72 | err("Failed to connect to the socket.\n"); 73 | } 74 | 75 | argc--, argv++; 76 | int msg_len = 0; 77 | 78 | for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) { 79 | n = snprintf(msg + offset, rem, "%s%c", *argv, 0); 80 | msg_len += n; 81 | } 82 | 83 | if (send(sock_fd, msg, msg_len, 0) == -1) { 84 | err("Failed to send the data.\n"); 85 | } 86 | 87 | int ret = EXIT_SUCCESS, nb; 88 | 89 | struct pollfd fds[] = { 90 | {sock_fd, POLLIN, 0}, 91 | {STDOUT_FILENO, POLLHUP, 0}, 92 | }; 93 | 94 | while (poll(fds, 2, -1) > 0) { 95 | if (fds[0].revents & POLLIN) { 96 | if ((nb = recv(sock_fd, rsp, sizeof(rsp)-1, 0)) > 0) { 97 | rsp[nb] = '\0'; 98 | if (rsp[0] == FAILURE_MESSAGE[0]) { 99 | ret = EXIT_FAILURE; 100 | fprintf(stderr, "%s", rsp + 1); 101 | fflush(stderr); 102 | } else { 103 | fprintf(stdout, "%s", rsp); 104 | fflush(stdout); 105 | } 106 | } else { 107 | break; 108 | } 109 | } 110 | if (fds[1].revents & (POLLERR | POLLHUP)) { 111 | break; 112 | } 113 | } 114 | 115 | close(sock_fd); 116 | return ret; 117 | } 118 | -------------------------------------------------------------------------------- /src/bspwm.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_BSPWM_H 26 | #define BSPWM_BSPWM_H 27 | 28 | #include "types.h" 29 | 30 | #define WM_NAME "bspwm" 31 | #define CONFIG_NAME WM_NAME "rc" 32 | #define CONFIG_HOME_ENV "XDG_CONFIG_HOME" 33 | #define RUNTIME_DIR_ENV "XDG_RUNTIME_DIR" 34 | 35 | #define STATE_PATH_TPL "/tmp/bspwm%s_%i_%i-state" 36 | 37 | #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) 38 | #define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE) 39 | #define BSPWM_CLASS_NAME "Bspwm" 40 | #define META_WINDOW_IC "wm\0" BSPWM_CLASS_NAME 41 | #define ROOT_WINDOW_IC "root\0" BSPWM_CLASS_NAME 42 | #define PRESEL_FEEDBACK_I "presel_feedback" 43 | #define PRESEL_FEEDBACK_IC PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME 44 | #define MOTION_RECORDER_I "motion_recorder" 45 | #define MOTION_RECORDER_IC MOTION_RECORDER_I "\0" BSPWM_CLASS_NAME 46 | 47 | typedef struct { 48 | xcb_window_t id; 49 | uint16_t sequence; 50 | bool enabled; 51 | } motion_recorder_t; 52 | 53 | extern xcb_connection_t *dpy; 54 | extern int default_screen, screen_width, screen_height; 55 | extern uint32_t clients_count; 56 | extern xcb_screen_t *screen; 57 | extern xcb_window_t root; 58 | extern char config_path[MAXLEN]; 59 | 60 | extern monitor_t *mon; 61 | extern monitor_t *mon_head; 62 | extern monitor_t *mon_tail; 63 | extern monitor_t *pri_mon; 64 | extern history_t *history_head; 65 | extern history_t *history_tail; 66 | extern history_t *history_needle; 67 | extern rule_t *rule_head; 68 | extern rule_t *rule_tail; 69 | extern stacking_list_t *stack_head; 70 | extern stacking_list_t *stack_tail; 71 | extern subscriber_list_t *subscribe_head; 72 | extern subscriber_list_t *subscribe_tail; 73 | extern pending_rule_t *pending_rule_head; 74 | extern pending_rule_t *pending_rule_tail; 75 | 76 | extern xcb_window_t meta_window; 77 | extern motion_recorder_t motion_recorder; 78 | extern xcb_atom_t WM_STATE; 79 | extern xcb_atom_t WM_TAKE_FOCUS; 80 | extern xcb_atom_t WM_DELETE_WINDOW; 81 | extern int exit_status; 82 | 83 | extern bool auto_raise; 84 | extern bool sticky_still; 85 | extern bool hide_sticky; 86 | extern bool record_history; 87 | extern bool running; 88 | extern bool restart; 89 | extern bool randr; 90 | 91 | void init(void); 92 | void setup(void); 93 | void register_events(void); 94 | void cleanup(void); 95 | bool check_connection (xcb_connection_t *dpy); 96 | void sig_handler(int sig); 97 | uint32_t get_color_pixel(const char *color); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_COMMON_H 26 | #define BSPWM_COMMON_H 27 | 28 | #define SOCKET_PATH_TPL "/tmp/bspwm%s_%i_%i-socket" 29 | #define SOCKET_ENV_VAR "BSPWM_SOCKET" 30 | 31 | #define FAILURE_MESSAGE "\x07" 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/desktop.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_DESKTOP_H 26 | #define BSPWM_DESKTOP_H 27 | 28 | #define DEFAULT_DESK_NAME "Desktop" 29 | 30 | bool activate_desktop(monitor_t *m, desktop_t *d); 31 | bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t *sel); 32 | bool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel); 33 | bool set_layout(monitor_t *m, desktop_t *d, layout_t l, bool user); 34 | void handle_presel_feedbacks(monitor_t *m, desktop_t *d); 35 | bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d, bool follow); 36 | desktop_t *make_desktop(const char *name, uint32_t id); 37 | void rename_desktop(monitor_t *m, desktop_t *d, const char *name); 38 | void insert_desktop(monitor_t *m, desktop_t *d); 39 | void add_desktop(monitor_t *m, desktop_t *d); 40 | desktop_t *find_desktop_in(uint32_t id, monitor_t *m); 41 | void unlink_desktop(monitor_t *m, desktop_t *d); 42 | void remove_desktop(monitor_t *m, desktop_t *d); 43 | void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd); 44 | bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2, bool follow); 45 | void show_desktop(desktop_t *d); 46 | void hide_desktop(desktop_t *d); 47 | bool is_urgent(desktop_t *d); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/events.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_EVENTS_H 26 | #define BSPWM_EVENTS_H 27 | 28 | #include 29 | #include 30 | 31 | #define ERROR_CODE_BAD_WINDOW 3 32 | 33 | extern uint8_t randr_base; 34 | static const xcb_button_index_t BUTTONS[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3}; 35 | 36 | void handle_event(xcb_generic_event_t *evt); 37 | void map_request(xcb_generic_event_t *evt); 38 | void configure_request(xcb_generic_event_t *evt); 39 | void configure_notify(xcb_generic_event_t *evt); 40 | void destroy_notify(xcb_generic_event_t *evt); 41 | void unmap_notify(xcb_generic_event_t *evt); 42 | void property_notify(xcb_generic_event_t *evt); 43 | void client_message(xcb_generic_event_t *evt); 44 | void focus_in(xcb_generic_event_t *evt); 45 | void button_press(xcb_generic_event_t *evt); 46 | void enter_notify(xcb_generic_event_t *evt); 47 | void motion_notify(xcb_generic_event_t *evt); 48 | void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action); 49 | void mapping_notify(xcb_generic_event_t *evt); 50 | void process_error(xcb_generic_event_t *evt); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/ewmh.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "bspwm.h" 30 | #include "settings.h" 31 | #include "tree.h" 32 | #include "ewmh.h" 33 | 34 | xcb_ewmh_connection_t *ewmh; 35 | 36 | void ewmh_init(void) 37 | { 38 | ewmh = calloc(1, sizeof(xcb_ewmh_connection_t)); 39 | if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) { 40 | err("Can't initialize EWMH atoms.\n"); 41 | } 42 | } 43 | 44 | void ewmh_update_active_window(void) 45 | { 46 | xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id); 47 | xcb_ewmh_set_active_window(ewmh, default_screen, win); 48 | } 49 | 50 | void ewmh_update_number_of_desktops(void) 51 | { 52 | uint32_t desktops_count = 0; 53 | 54 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 55 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 56 | desktops_count++; 57 | } 58 | } 59 | 60 | xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count); 61 | } 62 | 63 | uint32_t ewmh_get_desktop_index(desktop_t *d) 64 | { 65 | uint32_t i = 0; 66 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 67 | for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) { 68 | if (d == cd) { 69 | return i; 70 | } 71 | } 72 | } 73 | return 0; 74 | } 75 | 76 | bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc) 77 | { 78 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 79 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) { 80 | if (i == 0) { 81 | loc->monitor = m; 82 | loc->desktop = d; 83 | loc->node = NULL; 84 | return true; 85 | } 86 | } 87 | } 88 | return false; 89 | } 90 | 91 | void ewmh_update_current_desktop(void) 92 | { 93 | if (mon == NULL) { 94 | return; 95 | } 96 | uint32_t i = ewmh_get_desktop_index(mon->desk); 97 | xcb_ewmh_set_current_desktop(ewmh, default_screen, i); 98 | } 99 | 100 | void ewmh_set_wm_desktop(node_t *n, desktop_t *d) 101 | { 102 | uint32_t i = ewmh_get_desktop_index(d); 103 | for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) { 104 | if (f->client == NULL) { 105 | continue; 106 | } 107 | xcb_ewmh_set_wm_desktop(ewmh, f->id, i); 108 | } 109 | } 110 | 111 | void ewmh_update_wm_desktops(void) 112 | { 113 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 114 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 115 | uint32_t i = ewmh_get_desktop_index(d); 116 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { 117 | if (n->client == NULL) { 118 | continue; 119 | } 120 | xcb_ewmh_set_wm_desktop(ewmh, n->id, i); 121 | } 122 | } 123 | } 124 | } 125 | 126 | void ewmh_update_desktop_names(void) 127 | { 128 | char names[MAXLEN]; 129 | unsigned int i, j; 130 | uint32_t names_len; 131 | i = 0; 132 | 133 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 134 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 135 | for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) { 136 | names[i + j] = d->name[j]; 137 | } 138 | i += j; 139 | if (i < sizeof(names)) { 140 | names[i++] = '\0'; 141 | } 142 | } 143 | } 144 | 145 | if (i < 1) { 146 | xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL); 147 | return; 148 | } 149 | 150 | names_len = i - 1; 151 | xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names); 152 | } 153 | 154 | void ewmh_update_desktop_viewport(void) 155 | { 156 | uint32_t desktops_count = 0; 157 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 158 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 159 | desktops_count++; 160 | } 161 | } 162 | if (desktops_count == 0) { 163 | xcb_ewmh_set_desktop_viewport(ewmh, default_screen, 0, NULL); 164 | return; 165 | } 166 | xcb_ewmh_coordinates_t coords[desktops_count]; 167 | uint16_t desktop = 0; 168 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 169 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 170 | coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y}; 171 | } 172 | } 173 | xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords); 174 | } 175 | 176 | bool ewmh_handle_struts(xcb_window_t win) 177 | { 178 | xcb_ewmh_wm_strut_partial_t struts; 179 | bool changed = false; 180 | if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) { 181 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 182 | xcb_rectangle_t rect = m->rectangle; 183 | if (rect.x < (int16_t) struts.left && 184 | (int16_t) struts.left < (rect.x + rect.width - 1) && 185 | (int16_t) struts.left_end_y >= rect.y && 186 | (int16_t) struts.left_start_y < (rect.y + rect.height)) { 187 | int dx = struts.left - rect.x; 188 | if (m->padding.left < 0) { 189 | m->padding.left += dx; 190 | } else { 191 | m->padding.left = MAX(dx, m->padding.left); 192 | } 193 | changed = true; 194 | } 195 | if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) && 196 | (int16_t) (screen_width - struts.right) > rect.x && 197 | (int16_t) struts.right_end_y >= rect.y && 198 | (int16_t) struts.right_start_y < (rect.y + rect.height)) { 199 | int dx = (rect.x + rect.width) - screen_width + struts.right; 200 | if (m->padding.right < 0) { 201 | m->padding.right += dx; 202 | } else { 203 | m->padding.right = MAX(dx, m->padding.right); 204 | } 205 | changed = true; 206 | } 207 | if (rect.y < (int16_t) struts.top && 208 | (int16_t) struts.top < (rect.y + rect.height - 1) && 209 | (int16_t) struts.top_end_x >= rect.x && 210 | (int16_t) struts.top_start_x < (rect.x + rect.width)) { 211 | int dy = struts.top - rect.y; 212 | if (m->padding.top < 0) { 213 | m->padding.top += dy; 214 | } else { 215 | m->padding.top = MAX(dy, m->padding.top); 216 | } 217 | changed = true; 218 | } 219 | if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) && 220 | (int16_t) (screen_height - struts.bottom) > rect.y && 221 | (int16_t) struts.bottom_end_x >= rect.x && 222 | (int16_t) struts.bottom_start_x < (rect.x + rect.width)) { 223 | int dy = (rect.y + rect.height) - screen_height + struts.bottom; 224 | if (m->padding.bottom < 0) { 225 | m->padding.bottom += dy; 226 | } else { 227 | m->padding.bottom = MAX(dy, m->padding.bottom); 228 | } 229 | changed = true; 230 | } 231 | } 232 | } 233 | return changed; 234 | } 235 | 236 | void ewmh_update_client_list(bool stacking) 237 | { 238 | if (clients_count == 0) { 239 | xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL); 240 | xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL); 241 | return; 242 | } 243 | 244 | xcb_window_t wins[clients_count]; 245 | unsigned int i = 0; 246 | 247 | if (stacking) { 248 | for (stacking_list_t *s = stack_head; s != NULL; s = s->next) { 249 | wins[i++] = s->node->id; 250 | } 251 | xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins); 252 | } else { 253 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 254 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 255 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { 256 | if (n->client == NULL) { 257 | continue; 258 | } 259 | wins[i++] = n->id; 260 | } 261 | } 262 | } 263 | xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins); 264 | } 265 | } 266 | 267 | void ewmh_wm_state_update(node_t *n) 268 | { 269 | client_t *c = n->client; 270 | size_t count = 0; 271 | uint32_t values[12]; 272 | #define HANDLE_WM_STATE(s) \ 273 | if (WM_FLAG_##s & c->wm_flags) { \ 274 | values[count++] = ewmh->_NET_WM_STATE_##s; \ 275 | } 276 | HANDLE_WM_STATE(MODAL) 277 | HANDLE_WM_STATE(STICKY) 278 | HANDLE_WM_STATE(MAXIMIZED_VERT) 279 | HANDLE_WM_STATE(MAXIMIZED_HORZ) 280 | HANDLE_WM_STATE(SHADED) 281 | HANDLE_WM_STATE(SKIP_TASKBAR) 282 | HANDLE_WM_STATE(SKIP_PAGER) 283 | HANDLE_WM_STATE(HIDDEN) 284 | HANDLE_WM_STATE(FULLSCREEN) 285 | HANDLE_WM_STATE(ABOVE) 286 | HANDLE_WM_STATE(BELOW) 287 | HANDLE_WM_STATE(DEMANDS_ATTENTION) 288 | #undef HANDLE_WM_STATE 289 | xcb_ewmh_set_wm_state(ewmh, n->id, count, values); 290 | } 291 | 292 | void ewmh_set_supporting(xcb_window_t win) 293 | { 294 | pid_t wm_pid = getpid(); 295 | xcb_ewmh_set_supporting_wm_check(ewmh, root, win); 296 | xcb_ewmh_set_supporting_wm_check(ewmh, win, win); 297 | xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME); 298 | xcb_ewmh_set_wm_pid(ewmh, win, wm_pid); 299 | } 300 | -------------------------------------------------------------------------------- /src/ewmh.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_EWMH_H 26 | #define BSPWM_EWMH_H 27 | 28 | #include 29 | 30 | extern xcb_ewmh_connection_t *ewmh; 31 | 32 | void ewmh_init(void); 33 | void ewmh_update_active_window(void); 34 | void ewmh_update_number_of_desktops(void); 35 | uint32_t ewmh_get_desktop_index(desktop_t *d); 36 | bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc); 37 | void ewmh_update_current_desktop(void); 38 | void ewmh_set_wm_desktop(node_t *n, desktop_t *d); 39 | void ewmh_update_wm_desktops(void); 40 | void ewmh_update_desktop_names(void); 41 | void ewmh_update_desktop_viewport(void); 42 | bool ewmh_handle_struts(xcb_window_t win); 43 | void ewmh_update_client_list(bool stacking); 44 | void ewmh_wm_state_update(node_t *n); 45 | void ewmh_set_supporting(xcb_window_t win); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/geometry.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include "types.h" 27 | #include "settings.h" 28 | #include "geometry.h" 29 | 30 | bool is_inside(xcb_point_t p, xcb_rectangle_t r) 31 | { 32 | return (p.x >= r.x && p.x < (r.x + r.width) && 33 | p.y >= r.y && p.y < (r.y + r.height)); 34 | } 35 | 36 | /* Returns true if a contains b */ 37 | bool contains(xcb_rectangle_t a, xcb_rectangle_t b) 38 | { 39 | return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) && 40 | a.y <= b.y && (a.y + a.height) >= (b.y + b.height)); 41 | } 42 | 43 | unsigned int area(xcb_rectangle_t r) 44 | { 45 | return r.width * r.height; 46 | } 47 | 48 | /* Distance between the `dir` edge of `r1` and the `opposite(dir)` edge of `r2`. */ 49 | uint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir) 50 | { 51 | xcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1}; 52 | xcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1}; 53 | switch (dir) { 54 | case DIR_NORTH: 55 | return r2_max.y > r1.y ? r2_max.y - r1.y : r1.y - r2_max.y; 56 | break; 57 | case DIR_WEST: 58 | return r2_max.x > r1.x ? r2_max.x - r1.x : r1.x - r2_max.x; 59 | break; 60 | case DIR_SOUTH: 61 | return r2.y < r1_max.y ? r1_max.y - r2.y : r2.y - r1_max.y; 62 | break; 63 | case DIR_EAST: 64 | return r2.x < r1_max.x ? r1_max.x - r2.x : r2.x - r1_max.x; 65 | break; 66 | default: 67 | return UINT32_MAX; 68 | } 69 | } 70 | 71 | /* Is `r2` on the `dir` side of `r1`? */ 72 | bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir) 73 | { 74 | xcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1}; 75 | xcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1}; 76 | 77 | /* Eliminate rectangles on the opposite side */ 78 | switch (directional_focus_tightness) { 79 | case TIGHTNESS_LOW: 80 | switch (dir) { 81 | case DIR_NORTH: 82 | if (r2.y > r1_max.y) { 83 | return false; 84 | } 85 | break; 86 | case DIR_WEST: 87 | if (r2.x > r1_max.x) { 88 | return false; 89 | } 90 | break; 91 | case DIR_SOUTH: 92 | if (r2_max.y < r1.y) { 93 | return false; 94 | } 95 | break; 96 | case DIR_EAST: 97 | if (r2_max.x < r1.x) { 98 | return false; 99 | } 100 | break; 101 | default: 102 | return false; 103 | } 104 | break; 105 | case TIGHTNESS_HIGH: 106 | switch (dir) { 107 | case DIR_NORTH: 108 | if (r2.y >= r1.y) { 109 | return false; 110 | } 111 | break; 112 | case DIR_WEST: 113 | if (r2.x >= r1.x) { 114 | return false; 115 | } 116 | break; 117 | case DIR_SOUTH: 118 | if (r2_max.y <= r1_max.y) { 119 | return false; 120 | } 121 | break; 122 | case DIR_EAST: 123 | if (r2_max.x <= r1_max.x) { 124 | return false; 125 | } 126 | break; 127 | default: 128 | return false; 129 | } 130 | break; 131 | default: 132 | return false; 133 | } 134 | 135 | /* Is there a shared vertical/horizontal range? */ 136 | switch (dir) { 137 | case DIR_NORTH: 138 | case DIR_SOUTH: 139 | return 140 | (r2.x >= r1.x && r2.x <= r1_max.x) || 141 | (r2_max.x >= r1.x && r2_max.x <= r1_max.x) || 142 | (r1.x > r2.x && r1.x < r2_max.x); 143 | break; 144 | case DIR_WEST: 145 | case DIR_EAST: 146 | return 147 | (r2.y >= r1.y && r2.y <= r1_max.y) || 148 | (r2_max.y >= r1.y && r2_max.y <= r1_max.y) || 149 | (r1.y > r2.y && r1_max.y < r2_max.y); 150 | break; 151 | default: 152 | return false; 153 | } 154 | } 155 | 156 | bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b) 157 | { 158 | return (a.x == b.x && a.y == b.y && 159 | a.width == b.width && a.height == b.height); 160 | } 161 | 162 | int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2) 163 | { 164 | if (r1.y >= (r2.y + r2.height)) { 165 | return 1; 166 | } else if (r2.y >= (r1.y + r1.height)) { 167 | return -1; 168 | } else { 169 | if (r1.x >= (r2.x + r2.width)) { 170 | return 1; 171 | } else if (r2.x >= (r1.x + r1.width)) { 172 | return -1; 173 | } else { 174 | return area(r2) - area(r1); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/geometry.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_GEOMETRY_H 26 | #define BSPWM_GEOMETRY_H 27 | 28 | #include 29 | #include 30 | 31 | bool is_inside(xcb_point_t p, xcb_rectangle_t r); 32 | bool contains(xcb_rectangle_t a, xcb_rectangle_t b); 33 | unsigned int area(xcb_rectangle_t r); 34 | uint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir); 35 | bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir); 36 | bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b); 37 | int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/helpers.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "bspwm.h" 35 | 36 | void warn(char *fmt, ...) 37 | { 38 | va_list ap; 39 | va_start(ap, fmt); 40 | vfprintf(stderr, fmt, ap); 41 | va_end(ap); 42 | } 43 | 44 | __attribute__((noreturn)) 45 | void err(char *fmt, ...) 46 | { 47 | va_list ap; 48 | va_start(ap, fmt); 49 | vfprintf(stderr, fmt, ap); 50 | va_end(ap); 51 | exit(EXIT_FAILURE); 52 | } 53 | 54 | char *read_string(const char *file_path, size_t *tlen) 55 | { 56 | if (file_path == NULL) { 57 | return NULL; 58 | } 59 | 60 | int fd = open(file_path, O_RDONLY); 61 | 62 | if (fd == -1) { 63 | perror("Read file: open"); 64 | return NULL; 65 | } 66 | 67 | char buf[BUFSIZ], *content = NULL; 68 | size_t len = sizeof(buf); 69 | 70 | if ((content = calloc(len, sizeof(char))) == NULL) { 71 | perror("Read file: calloc"); 72 | goto end; 73 | } 74 | 75 | int nb; 76 | *tlen = 0; 77 | 78 | while (true) { 79 | nb = read(fd, buf, sizeof(buf)); 80 | if (nb < 0) { 81 | perror("Restore tree: read"); 82 | free(content); 83 | content = NULL; 84 | goto end; 85 | } else if (nb == 0) { 86 | break; 87 | } else { 88 | *tlen += nb; 89 | if (*tlen > len) { 90 | len *= 2; 91 | char *rcontent = realloc(content, len * sizeof(char)); 92 | if (rcontent == NULL) { 93 | perror("Read file: realloc"); 94 | free(content); 95 | content = NULL; 96 | goto end; 97 | } else { 98 | content = rcontent; 99 | } 100 | } 101 | strncpy(content + (*tlen - nb), buf, nb); 102 | } 103 | } 104 | 105 | end: 106 | close(fd); 107 | return content; 108 | } 109 | 110 | char *copy_string(char *str, size_t len) 111 | { 112 | char *cpy = calloc(1, ((len+1) * sizeof(char))); 113 | if (cpy == NULL) { 114 | perror("Copy string: calloc"); 115 | return NULL; 116 | } 117 | strncpy(cpy, str, len); 118 | cpy[len] = '\0'; 119 | return cpy; 120 | } 121 | 122 | char *mktempfifo(const char *template) 123 | { 124 | int tempfd; 125 | char *runtime_dir = getenv(RUNTIME_DIR_ENV); 126 | if (runtime_dir == NULL) { 127 | runtime_dir = "/tmp"; 128 | } 129 | 130 | char *fifo_path = malloc(strlen(runtime_dir)+1+strlen(template)+1); 131 | if (fifo_path == NULL) { 132 | return NULL; 133 | } 134 | 135 | sprintf(fifo_path, "%s/%s", runtime_dir, template); 136 | 137 | if ((tempfd = mkstemp(fifo_path)) == -1) { 138 | free(fifo_path); 139 | return NULL; 140 | } 141 | 142 | close(tempfd); 143 | unlink(fifo_path); 144 | 145 | if (mkfifo(fifo_path, 0666) == -1) { 146 | free(fifo_path); 147 | return NULL; 148 | } 149 | 150 | return fifo_path; 151 | } 152 | 153 | int asprintf(char **buf, const char *fmt, ...) 154 | { 155 | va_list args; 156 | va_start(args, fmt); 157 | int size = vasprintf(buf, fmt, args); 158 | va_end(args); 159 | return size; 160 | } 161 | 162 | int vasprintf(char **buf, const char *fmt, va_list args) 163 | { 164 | va_list tmp; 165 | va_copy(tmp, args); 166 | int size = vsnprintf(NULL, 0, fmt, tmp); 167 | va_end(tmp); 168 | 169 | if (size < 0) { 170 | return -1; 171 | } 172 | 173 | *buf = malloc(size + 1); 174 | 175 | if (*buf == NULL) { 176 | return -1; 177 | } 178 | 179 | size = vsprintf(*buf, fmt, args); 180 | return size; 181 | } 182 | 183 | bool is_hex_color(const char *color) 184 | { 185 | if (color[0] != '#' || strlen(color) != 7) { 186 | return false; 187 | } 188 | for (int i = 1; i < 7; i++) { 189 | if (!isxdigit(color[i])) { 190 | return false; 191 | } 192 | } 193 | return true; 194 | } 195 | 196 | char *tokenize_with_escape(struct tokenize_state *state, const char *s, char sep) 197 | { 198 | if (s != NULL) { 199 | // first call 200 | state->in_escape = false; 201 | state->pos = s; 202 | state->len = strlen(s) + 1; 203 | } 204 | 205 | char *outp = calloc(state->len, 1); 206 | char *ret = outp; 207 | if (!outp) return NULL; 208 | 209 | char cur; 210 | while (*state->pos) { 211 | --state->len; 212 | cur = *state->pos++; 213 | 214 | if (state->in_escape) { 215 | *outp++ = cur; 216 | state->in_escape = false; 217 | continue; 218 | } 219 | 220 | if (cur == '\\') { 221 | state->in_escape = !state->in_escape; 222 | } else if (cur == sep) { 223 | return ret; 224 | } else { 225 | *outp++ = cur; 226 | } 227 | } 228 | 229 | return ret; 230 | } 231 | -------------------------------------------------------------------------------- /src/helpers.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_HELPERS_H 26 | #define BSPWM_HELPERS_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define LENGTH(x) (sizeof(x) / sizeof(*x)) 36 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 37 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 38 | 39 | #define IS_TILED(c) (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED) 40 | #define IS_FLOATING(c) (c->state == STATE_FLOATING) 41 | #define IS_FULLSCREEN(c) (c->state == STATE_FULLSCREEN) 42 | #define IS_RECEPTACLE(n) (is_leaf(n) && n->client == NULL) 43 | 44 | #define BOOL_STR(A) ((A) ? "true" : "false") 45 | #define ON_OFF_STR(A) ((A) ? "on" : "off") 46 | #define LAYOUT_STR(A) ((A) == LAYOUT_TILED ? "tiled" : "monocle") 47 | #define LAYOUT_CHR(A) ((A) == LAYOUT_TILED ? 'T' : 'M') 48 | #define CHILD_POL_STR(A) ((A) == FIRST_CHILD ? "first_child" : "second_child") 49 | #define AUTO_SCM_STR(A) ((A) == SCHEME_LONGEST_SIDE ? "longest_side" : ((A) == SCHEME_ALTERNATE ? "alternate" : "spiral")) 50 | #define TIGHTNESS_STR(A) ((A) == TIGHTNESS_HIGH ? "high" : "low") 51 | #define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical") 52 | #define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual") 53 | #define SPLIT_DIR_STR(A) ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east"))) 54 | #define STATE_STR(A) ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled"))) 55 | #define STATE_CHR(A) ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P'))) 56 | #define LAYER_STR(A) ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above")) 57 | #define HSH_MODE_STR(A) ((A) == HONOR_SIZE_HINTS_TILED ? "tiled" : ((A) == HONOR_SIZE_HINTS_FLOATING ? "floating" : BOOL_STR(A))) 58 | 59 | #define XCB_CONFIG_WINDOW_X_Y (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y) 60 | #define XCB_CONFIG_WINDOW_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT) 61 | #define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT) 62 | 63 | #define SHOULD_HONOR_SIZE_HINTS(hsh, state) \ 64 | ((hsh) && \ 65 | (((hsh) == HONOR_SIZE_HINTS_YES && \ 66 | (state) != STATE_FULLSCREEN) || \ 67 | ((hsh) == HONOR_SIZE_HINTS_TILED && \ 68 | (state) == STATE_TILED) || \ 69 | ((hsh) == HONOR_SIZE_HINTS_FLOATING && \ 70 | ((state) == STATE_FLOATING || (state) == STATE_PSEUDO_TILED)))) 71 | 72 | #define MAXLEN 256 73 | #define SMALEN 32 74 | #define INIT_CAP 8 75 | 76 | #define cleaned_mask(m) (m & ~(num_lock | scroll_lock | caps_lock)) 77 | #define streq(s1, s2) (strcmp((s1), (s2)) == 0) 78 | #define unsigned_subtract(a, b) \ 79 | do { \ 80 | if (b > a) { \ 81 | a = 0; \ 82 | } else { \ 83 | a -= b; \ 84 | } \ 85 | } while (false) 86 | 87 | 88 | void warn(char *fmt, ...); 89 | void err(char *fmt, ...); 90 | char *read_string(const char *file_path, size_t *tlen); 91 | char *copy_string(char *str, size_t len); 92 | char *mktempfifo(const char *template); 93 | int asprintf(char **buf, const char *fmt, ...); 94 | int vasprintf(char **buf, const char *fmt, va_list args); 95 | bool is_hex_color(const char *color); 96 | 97 | struct tokenize_state { 98 | bool in_escape; 99 | const char *pos; 100 | size_t len; 101 | }; 102 | char *tokenize_with_escape(struct tokenize_state *state, const char *s, char sep); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/history.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include "bspwm.h" 28 | #include "tree.h" 29 | #include "query.h" 30 | #include "history.h" 31 | 32 | history_t *make_history(monitor_t *m, desktop_t *d, node_t *n) 33 | { 34 | history_t *h = calloc(1, sizeof(history_t)); 35 | h->loc = (coordinates_t) {m, d, n}; 36 | h->prev = h->next = NULL; 37 | h->latest = true; 38 | return h; 39 | } 40 | 41 | void history_add(monitor_t *m, desktop_t *d, node_t *n, bool focused) 42 | { 43 | if (!record_history) { 44 | return; 45 | } 46 | 47 | if (focused) { 48 | history_needle = NULL; 49 | } 50 | 51 | history_t *h = make_history(m, d, n); 52 | 53 | if (history_head == NULL) { 54 | history_head = history_tail = h; 55 | } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) { 56 | history_t *ip = focused ? history_tail : NULL; 57 | 58 | for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) { 59 | if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) { 60 | hh->latest = false; 61 | } 62 | if (ip == NULL && ((n != NULL && hh->loc.desktop == d) || (n == NULL && hh->loc.monitor == m))) { 63 | ip = hh; 64 | } 65 | } 66 | 67 | if (ip != NULL) { 68 | history_insert_after(h, ip); 69 | } else { 70 | ip = history_head; 71 | if (n != NULL) { 72 | for (history_t *hh = history_head; hh != NULL; hh = hh->next) { 73 | if (hh->latest && hh->loc.monitor == m) { 74 | ip = hh; 75 | break; 76 | } 77 | } 78 | } 79 | history_insert_before(h, ip); 80 | } 81 | } else { 82 | free(h); 83 | } 84 | } 85 | 86 | // Inserts `a` after `b`. 87 | void history_insert_after(history_t *a, history_t *b) 88 | { 89 | a->next = b->next; 90 | if (b->next != NULL) { 91 | b->next->prev = a; 92 | } 93 | b->next = a; 94 | a->prev = b; 95 | if (history_tail == b) { 96 | history_tail = a; 97 | } 98 | } 99 | 100 | // Inserts `a` before `b`. 101 | void history_insert_before(history_t *a, history_t *b) 102 | { 103 | a->prev = b->prev; 104 | if (b->prev != NULL) { 105 | b->prev->next = a; 106 | } 107 | b->prev = a; 108 | a->next = b; 109 | if (history_head == b) { 110 | history_head = a; 111 | } 112 | } 113 | 114 | void history_remove(desktop_t *d, node_t *n, bool deep) 115 | { 116 | /* removing from the newest to the oldest is required */ 117 | /* for maintaining the *latest* attribute */ 118 | history_t *b = history_tail; 119 | while (b != NULL) { 120 | if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) || 121 | (n == NULL && d == b->loc.desktop)) { 122 | history_t *a = b->next; 123 | history_t *c = b->prev; 124 | if (a != NULL) { 125 | /* remove duplicate entries */ 126 | while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) || 127 | (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) { 128 | history_t *p = c->prev; 129 | if (history_head == c) { 130 | history_head = history_tail; 131 | } 132 | if (history_needle == c) { 133 | history_needle = history_tail; 134 | } 135 | free(c); 136 | c = p; 137 | } 138 | a->prev = c; 139 | } 140 | if (c != NULL) { 141 | c->next = a; 142 | } 143 | if (history_tail == b) { 144 | history_tail = c; 145 | } 146 | if (history_head == b) { 147 | history_head = a; 148 | } 149 | if (history_needle == b) { 150 | history_needle = c; 151 | } 152 | free(b); 153 | b = c; 154 | } else { 155 | b = b->prev; 156 | } 157 | } 158 | } 159 | 160 | void empty_history(void) 161 | { 162 | history_t *h = history_head; 163 | while (h != NULL) { 164 | history_t *next = h->next; 165 | free(h); 166 | h = next; 167 | } 168 | history_head = history_tail = NULL; 169 | } 170 | 171 | node_t *history_last_node(desktop_t *d, node_t *n) 172 | { 173 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 174 | if (h->latest && h->loc.node != NULL && !h->loc.node->hidden && 175 | !is_descendant(h->loc.node, n) && h->loc.desktop == d) { 176 | return h->loc.node; 177 | } 178 | } 179 | return NULL; 180 | } 181 | 182 | desktop_t *history_last_desktop(monitor_t *m, desktop_t *d) 183 | { 184 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 185 | if (h->latest && h->loc.desktop != d && h->loc.monitor == m) { 186 | return h->loc.desktop; 187 | } 188 | } 189 | return NULL; 190 | } 191 | 192 | monitor_t *history_last_monitor(monitor_t *m) 193 | { 194 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 195 | if (h->latest && h->loc.monitor != m) { 196 | return h->loc.monitor; 197 | } 198 | } 199 | return NULL; 200 | } 201 | 202 | bool history_find_newest_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel) 203 | { 204 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 205 | if (h->loc.node == NULL || 206 | h->loc.node->hidden || 207 | !node_matches(&h->loc, ref, sel)) { 208 | continue; 209 | } 210 | *dst = h->loc; 211 | return true; 212 | } 213 | 214 | return false; 215 | } 216 | 217 | bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t *sel) 218 | { 219 | if (history_needle == NULL || record_history) { 220 | history_needle = history_tail; 221 | } 222 | 223 | history_t *h; 224 | for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) { 225 | if (!h->latest || 226 | h->loc.node == NULL || 227 | h->loc.node == ref->node || 228 | h->loc.node->hidden || 229 | !node_matches(&h->loc, ref, sel)) { 230 | continue; 231 | } 232 | if (!record_history) { 233 | history_needle = h; 234 | } 235 | *dst = h->loc; 236 | return true; 237 | } 238 | return false; 239 | } 240 | 241 | bool history_find_newest_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel) 242 | { 243 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 244 | if (desktop_matches(&h->loc, ref, sel)) { 245 | *dst = h->loc; 246 | return true; 247 | } 248 | } 249 | 250 | return false; 251 | } 252 | 253 | bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel) 254 | { 255 | if (history_needle == NULL || record_history) { 256 | history_needle = history_tail; 257 | } 258 | 259 | history_t *h; 260 | for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) { 261 | if (!h->latest || 262 | h->loc.desktop == ref->desktop || 263 | !desktop_matches(&h->loc, ref, sel)) { 264 | continue; 265 | } 266 | if (!record_history) { 267 | history_needle = h; 268 | } 269 | *dst = h->loc; 270 | return true; 271 | } 272 | return false; 273 | } 274 | 275 | bool history_find_newest_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel) 276 | { 277 | for (history_t *h = history_tail; h != NULL; h = h->prev) { 278 | if (monitor_matches(&h->loc, ref, sel)) { 279 | *dst = h->loc; 280 | return true; 281 | } 282 | } 283 | 284 | return false; 285 | } 286 | 287 | bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel) 288 | { 289 | if (history_needle == NULL || record_history) { 290 | history_needle = history_tail; 291 | } 292 | 293 | history_t *h; 294 | 295 | for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) { 296 | if (!h->latest || 297 | h->loc.monitor == ref->monitor || 298 | !monitor_matches(&h->loc, ref, sel)) { 299 | continue; 300 | } 301 | if (!record_history) { 302 | history_needle = h; 303 | } 304 | *dst = h->loc; 305 | return true; 306 | } 307 | 308 | return false; 309 | } 310 | 311 | uint32_t history_rank(node_t *n) 312 | { 313 | uint32_t r = 0; 314 | history_t *h = history_tail; 315 | while (h != NULL && (!h->latest || h->loc.node != n)) { 316 | h = h->prev; 317 | r++; 318 | } 319 | if (h == NULL) { 320 | return UINT32_MAX; 321 | } else { 322 | return r; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/history.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_HISTORY_H 26 | #define BSPWM_HISTORY_H 27 | 28 | #include "types.h" 29 | 30 | history_t *make_history(monitor_t *m, desktop_t *d, node_t *n); 31 | void history_add(monitor_t *m, desktop_t *d, node_t *n, bool focused); 32 | void history_insert_after(history_t *a, history_t *b); 33 | void history_insert_before(history_t *a, history_t *b); 34 | void history_remove(desktop_t *d, node_t *n, bool deep); 35 | void empty_history(void); 36 | node_t *history_last_node(desktop_t *d, node_t *n); 37 | desktop_t *history_last_desktop(monitor_t *m, desktop_t *d); 38 | monitor_t *history_last_monitor(monitor_t *m); 39 | bool history_find_newest_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 40 | bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 41 | bool history_find_newest_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel); 42 | bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel); 43 | bool history_find_newest_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel); 44 | bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel); 45 | uint32_t history_rank(node_t *n); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/jsmn.c: -------------------------------------------------------------------------------- 1 | #include "jsmn.h" 2 | 3 | /** 4 | * Allocates a fresh unused token from the token pool. 5 | */ 6 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 7 | jsmntok_t *tokens, size_t num_tokens) { 8 | jsmntok_t *tok; 9 | if (parser->toknext >= num_tokens) { 10 | return NULL; 11 | } 12 | tok = &tokens[parser->toknext++]; 13 | tok->start = tok->end = -1; 14 | tok->size = 0; 15 | #ifdef JSMN_PARENT_LINKS 16 | tok->parent = -1; 17 | #endif 18 | return tok; 19 | } 20 | 21 | /** 22 | * Fills token type and boundaries. 23 | */ 24 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 25 | int start, int end) { 26 | token->type = type; 27 | token->start = start; 28 | token->end = end; 29 | token->size = 0; 30 | } 31 | 32 | /** 33 | * Fills next available token with JSON primitive. 34 | */ 35 | static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, 36 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 37 | jsmntok_t *token; 38 | int start; 39 | 40 | start = parser->pos; 41 | 42 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 43 | switch (js[parser->pos]) { 44 | #ifndef JSMN_STRICT 45 | /* In strict mode primitive must be followed by "," or "}" or "]" */ 46 | case ':': 47 | #endif 48 | case '\t' : case '\r' : case '\n' : case ' ' : 49 | case ',' : case ']' : case '}' : 50 | goto found; 51 | } 52 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 53 | parser->pos = start; 54 | return JSMN_ERROR_INVAL; 55 | } 56 | } 57 | #ifdef JSMN_STRICT 58 | /* In strict mode primitive must be followed by a comma/object/array */ 59 | parser->pos = start; 60 | return JSMN_ERROR_PART; 61 | #endif 62 | 63 | found: 64 | if (tokens == NULL) { 65 | parser->pos--; 66 | return 0; 67 | } 68 | token = jsmn_alloc_token(parser, tokens, num_tokens); 69 | if (token == NULL) { 70 | parser->pos = start; 71 | return JSMN_ERROR_NOMEM; 72 | } 73 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 74 | #ifdef JSMN_PARENT_LINKS 75 | token->parent = parser->toksuper; 76 | #endif 77 | parser->pos--; 78 | return 0; 79 | } 80 | 81 | /** 82 | * Fills next token with JSON string. 83 | */ 84 | static int jsmn_parse_string(jsmn_parser *parser, const char *js, 85 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 86 | jsmntok_t *token; 87 | 88 | int start = parser->pos; 89 | 90 | parser->pos++; 91 | 92 | /* Skip starting quote */ 93 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 94 | char c = js[parser->pos]; 95 | 96 | /* Quote: end of string */ 97 | if (c == '\"') { 98 | if (tokens == NULL) { 99 | return 0; 100 | } 101 | token = jsmn_alloc_token(parser, tokens, num_tokens); 102 | if (token == NULL) { 103 | parser->pos = start; 104 | return JSMN_ERROR_NOMEM; 105 | } 106 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); 107 | #ifdef JSMN_PARENT_LINKS 108 | token->parent = parser->toksuper; 109 | #endif 110 | return 0; 111 | } 112 | 113 | /* Backslash: Quoted symbol expected */ 114 | if (c == '\\' && parser->pos + 1 < len) { 115 | int i; 116 | parser->pos++; 117 | switch (js[parser->pos]) { 118 | /* Allowed escaped symbols */ 119 | case '\"': case '/' : case '\\' : case 'b' : 120 | case 'f' : case 'r' : case 'n' : case 't' : 121 | break; 122 | /* Allows escaped symbol \uXXXX */ 123 | case 'u': 124 | parser->pos++; 125 | for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { 126 | /* If it isn't a hex character we have an error */ 127 | if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 128 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 129 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 130 | parser->pos = start; 131 | return JSMN_ERROR_INVAL; 132 | } 133 | parser->pos++; 134 | } 135 | parser->pos--; 136 | break; 137 | /* Unexpected symbol */ 138 | default: 139 | parser->pos = start; 140 | return JSMN_ERROR_INVAL; 141 | } 142 | } 143 | } 144 | parser->pos = start; 145 | return JSMN_ERROR_PART; 146 | } 147 | 148 | /** 149 | * Parse JSON string and fill tokens. 150 | */ 151 | int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 152 | jsmntok_t *tokens, unsigned int num_tokens) { 153 | int r; 154 | int i; 155 | jsmntok_t *token; 156 | int count = parser->toknext; 157 | 158 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 159 | char c; 160 | jsmntype_t type; 161 | 162 | c = js[parser->pos]; 163 | switch (c) { 164 | case '{': case '[': 165 | count++; 166 | if (tokens == NULL) { 167 | break; 168 | } 169 | token = jsmn_alloc_token(parser, tokens, num_tokens); 170 | if (token == NULL) 171 | return JSMN_ERROR_NOMEM; 172 | if (parser->toksuper != -1) { 173 | tokens[parser->toksuper].size++; 174 | #ifdef JSMN_PARENT_LINKS 175 | token->parent = parser->toksuper; 176 | #endif 177 | } 178 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 179 | token->start = parser->pos; 180 | parser->toksuper = parser->toknext - 1; 181 | break; 182 | case '}': case ']': 183 | if (tokens == NULL) 184 | break; 185 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 186 | #ifdef JSMN_PARENT_LINKS 187 | if (parser->toknext < 1) { 188 | return JSMN_ERROR_INVAL; 189 | } 190 | token = &tokens[parser->toknext - 1]; 191 | for (;;) { 192 | if (token->start != -1 && token->end == -1) { 193 | if (token->type != type) { 194 | return JSMN_ERROR_INVAL; 195 | } 196 | token->end = parser->pos + 1; 197 | parser->toksuper = token->parent; 198 | break; 199 | } 200 | if (token->parent == -1) { 201 | if(token->type != type || parser->toksuper == -1) { 202 | return JSMN_ERROR_INVAL; 203 | } 204 | break; 205 | } 206 | token = &tokens[token->parent]; 207 | } 208 | #else 209 | for (i = parser->toknext - 1; i >= 0; i--) { 210 | token = &tokens[i]; 211 | if (token->start != -1 && token->end == -1) { 212 | if (token->type != type) { 213 | return JSMN_ERROR_INVAL; 214 | } 215 | parser->toksuper = -1; 216 | token->end = parser->pos + 1; 217 | break; 218 | } 219 | } 220 | /* Error if unmatched closing bracket */ 221 | if (i == -1) return JSMN_ERROR_INVAL; 222 | for (; i >= 0; i--) { 223 | token = &tokens[i]; 224 | if (token->start != -1 && token->end == -1) { 225 | parser->toksuper = i; 226 | break; 227 | } 228 | } 229 | #endif 230 | break; 231 | case '\"': 232 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 233 | if (r < 0) return r; 234 | count++; 235 | if (parser->toksuper != -1 && tokens != NULL) 236 | tokens[parser->toksuper].size++; 237 | break; 238 | case '\t' : case '\r' : case '\n' : case ' ': 239 | break; 240 | case ':': 241 | parser->toksuper = parser->toknext - 1; 242 | break; 243 | case ',': 244 | if (tokens != NULL && parser->toksuper != -1 && 245 | tokens[parser->toksuper].type != JSMN_ARRAY && 246 | tokens[parser->toksuper].type != JSMN_OBJECT) { 247 | #ifdef JSMN_PARENT_LINKS 248 | parser->toksuper = tokens[parser->toksuper].parent; 249 | #else 250 | for (i = parser->toknext - 1; i >= 0; i--) { 251 | if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 252 | if (tokens[i].start != -1 && tokens[i].end == -1) { 253 | parser->toksuper = i; 254 | break; 255 | } 256 | } 257 | } 258 | #endif 259 | } 260 | break; 261 | #ifdef JSMN_STRICT 262 | /* In strict mode primitives are: numbers and booleans */ 263 | case '-': case '0': case '1' : case '2': case '3' : case '4': 264 | case '5': case '6': case '7' : case '8': case '9': 265 | case 't': case 'f': case 'n' : 266 | /* And they must not be keys of the object */ 267 | if (tokens != NULL && parser->toksuper != -1) { 268 | jsmntok_t *t = &tokens[parser->toksuper]; 269 | if (t->type == JSMN_OBJECT || 270 | (t->type == JSMN_STRING && t->size != 0)) { 271 | return JSMN_ERROR_INVAL; 272 | } 273 | } 274 | #else 275 | /* In non-strict mode every unquoted value is a primitive */ 276 | default: 277 | #endif 278 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 279 | if (r < 0) return r; 280 | count++; 281 | if (parser->toksuper != -1 && tokens != NULL) 282 | tokens[parser->toksuper].size++; 283 | break; 284 | 285 | #ifdef JSMN_STRICT 286 | /* Unexpected char in strict mode */ 287 | default: 288 | return JSMN_ERROR_INVAL; 289 | #endif 290 | } 291 | } 292 | 293 | if (tokens != NULL) { 294 | for (i = parser->toknext - 1; i >= 0; i--) { 295 | /* Unmatched opened object or array */ 296 | if (tokens[i].start != -1 && tokens[i].end == -1) { 297 | return JSMN_ERROR_PART; 298 | } 299 | } 300 | } 301 | 302 | return count; 303 | } 304 | 305 | /** 306 | * Creates a new parser based over a given buffer with an array of tokens 307 | * available. 308 | */ 309 | void jsmn_init(jsmn_parser *parser) { 310 | parser->pos = 0; 311 | parser->toknext = 0; 312 | parser->toksuper = -1; 313 | } 314 | 315 | -------------------------------------------------------------------------------- /src/jsmn.h: -------------------------------------------------------------------------------- 1 | #ifndef __JSMN_H_ 2 | #define __JSMN_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * JSON type identifier. Basic types are: 12 | * o Object 13 | * o Array 14 | * o String 15 | * o Other primitive: number, boolean (true/false) or null 16 | */ 17 | typedef enum { 18 | JSMN_UNDEFINED = 0, 19 | JSMN_OBJECT = 1, 20 | JSMN_ARRAY = 2, 21 | JSMN_STRING = 3, 22 | JSMN_PRIMITIVE = 4 23 | } jsmntype_t; 24 | 25 | enum jsmnerr { 26 | /* Not enough tokens were provided */ 27 | JSMN_ERROR_NOMEM = -1, 28 | /* Invalid character inside JSON string */ 29 | JSMN_ERROR_INVAL = -2, 30 | /* The string is not a full JSON packet, more bytes expected */ 31 | JSMN_ERROR_PART = -3 32 | }; 33 | 34 | /** 35 | * JSON token description. 36 | * type type (object, array, string etc.) 37 | * start start position in JSON data string 38 | * end end position in JSON data string 39 | */ 40 | typedef struct { 41 | jsmntype_t type; 42 | int start; 43 | int end; 44 | int size; 45 | #ifdef JSMN_PARENT_LINKS 46 | int parent; 47 | #endif 48 | } jsmntok_t; 49 | 50 | /** 51 | * JSON parser. Contains an array of token blocks available. Also stores 52 | * the string being parsed now and current position in that string 53 | */ 54 | typedef struct { 55 | unsigned int pos; /* offset in the JSON string */ 56 | unsigned int toknext; /* next token to allocate */ 57 | int toksuper; /* superior token node, e.g parent object or array */ 58 | } jsmn_parser; 59 | 60 | /** 61 | * Create JSON parser over an array of tokens 62 | */ 63 | void jsmn_init(jsmn_parser *parser); 64 | 65 | /** 66 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 67 | * a single JSON object. 68 | */ 69 | int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 70 | jsmntok_t *tokens, unsigned int num_tokens); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif /* __JSMN_H_ */ 77 | -------------------------------------------------------------------------------- /src/messages.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_MESSAGES_H 26 | #define BSPWM_MESSAGES_H 27 | 28 | #include "types.h" 29 | #include "subscribe.h" 30 | 31 | void handle_message(char *msg, int msg_len, FILE *rsp); 32 | void process_message(char **args, int num, FILE *rsp); 33 | void cmd_node(char **args, int num, FILE *rsp); 34 | void cmd_desktop(char **args, int num, FILE *rsp); 35 | void cmd_monitor(char **args, int num, FILE *rsp); 36 | void cmd_query(char **args, int num, FILE *rsp); 37 | void cmd_rule(char **args, int num, FILE *rsp); 38 | void cmd_wm(char **args, int num, FILE *rsp); 39 | void cmd_subscribe(char **args, int num, FILE *rsp); 40 | void cmd_quit(char **args, int num, FILE *rsp); 41 | void cmd_config(char **args, int num, FILE *rsp); 42 | void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp); 43 | void get_setting(coordinates_t loc, char *name, FILE* rsp); 44 | void handle_failure(int code, char *src, char *val, FILE *rsp); 45 | void fail(FILE *rsp, char *fmt, ...); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/monitor.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_MONITOR_H 26 | #define BSPWM_MONITOR_H 27 | 28 | #define DEFAULT_MON_NAME "MONITOR" 29 | 30 | monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id); 31 | void update_root(monitor_t *m, xcb_rectangle_t *rect); 32 | void reorder_monitor(monitor_t *m); 33 | void rename_monitor(monitor_t *m, const char *name); 34 | monitor_t *find_monitor(uint32_t id); 35 | monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id); 36 | void embrace_client(monitor_t *m, client_t *c); 37 | void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n); 38 | void add_monitor(monitor_t *m); 39 | void unlink_monitor(monitor_t *m); 40 | void remove_monitor(monitor_t *m); 41 | void merge_monitors(monitor_t *ms, monitor_t *md); 42 | bool swap_monitors(monitor_t *m1, monitor_t *m2); 43 | monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t *sel); 44 | bool is_inside_monitor(monitor_t *m, xcb_point_t pt); 45 | monitor_t *monitor_from_point(xcb_point_t pt); 46 | monitor_t *monitor_from_client(client_t *c); 47 | monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t *sel); 48 | bool find_any_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel); 49 | bool update_monitors(void); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef BSPWM_PARSE_H 2 | #define BSPWM_PARSE_H 3 | 4 | #include "types.h" 5 | #include "subscribe.h" 6 | 7 | #define OPT_CHR '-' 8 | #define CAT_CHR '.' 9 | #define EQL_TOK "=" 10 | #define COL_TOK ":" 11 | 12 | bool parse_bool(char *value, bool *b); 13 | bool parse_split_type(char *s, split_type_t *t); 14 | bool parse_split_mode(char *s, split_mode_t *m); 15 | bool parse_layout(char *s, layout_t *l); 16 | bool parse_client_state(char *s, client_state_t *t); 17 | bool parse_stack_layer(char *s, stack_layer_t *l); 18 | bool parse_direction(char *s, direction_t *d); 19 | bool parse_cycle_direction(char *s, cycle_dir_t *d); 20 | bool parse_circulate_direction(char *s, circulate_dir_t *d); 21 | bool parse_history_direction(char *s, history_dir_t *d); 22 | bool parse_flip(char *s, flip_t *f); 23 | bool parse_resize_handle(char *s, resize_handle_t *h); 24 | bool parse_modifier_mask(char *s, uint16_t *m); 25 | bool parse_button_index(char *s, int8_t *b); 26 | bool parse_pointer_action(char *s, pointer_action_t *a); 27 | bool parse_child_polarity(char *s, child_polarity_t *p); 28 | bool parse_automatic_scheme(char *s, automatic_scheme_t *a); 29 | bool parse_honor_size_hints_mode(char *s, honor_size_hints_mode_t *a); 30 | bool parse_state_transition(char *s, state_transition_t *m); 31 | bool parse_tightness(char *s, tightness_t *t); 32 | bool parse_degree(char *s, int *d); 33 | bool parse_id(char *s, uint32_t *id); 34 | bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state); 35 | bool parse_index(char *s, uint16_t *idx); 36 | bool parse_rectangle(char *s, xcb_rectangle_t *r); 37 | bool parse_subscriber_mask(char *s, subscriber_mask_t *mask); 38 | bool parse_monitor_modifiers(char *desc, monitor_select_t *sel); 39 | bool parse_desktop_modifiers(char *desc, desktop_select_t *sel); 40 | bool parse_node_modifiers(char *desc, node_select_t *sel); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/pointer.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include "bspwm.h" 29 | #include "query.h" 30 | #include "settings.h" 31 | #include "stack.h" 32 | #include "tree.h" 33 | #include "monitor.h" 34 | #include "subscribe.h" 35 | #include "events.h" 36 | #include "window.h" 37 | #include "pointer.h" 38 | 39 | uint16_t num_lock; 40 | uint16_t caps_lock; 41 | uint16_t scroll_lock; 42 | 43 | bool grabbing; 44 | node_t *grabbed_node; 45 | 46 | void pointer_init(void) 47 | { 48 | num_lock = modfield_from_keysym(XK_Num_Lock); 49 | caps_lock = modfield_from_keysym(XK_Caps_Lock); 50 | scroll_lock = modfield_from_keysym(XK_Scroll_Lock); 51 | if (caps_lock == XCB_NO_SYMBOL) { 52 | caps_lock = XCB_MOD_MASK_LOCK; 53 | } 54 | grabbing = false; 55 | grabbed_node = NULL; 56 | } 57 | 58 | void window_grab_buttons(xcb_window_t win) 59 | { 60 | for (unsigned int i = 0; i < LENGTH(BUTTONS); i++) { 61 | if (click_to_focus == (int8_t) XCB_BUTTON_INDEX_ANY || click_to_focus == (int8_t) BUTTONS[i]) { 62 | window_grab_button(win, BUTTONS[i], XCB_NONE); 63 | } 64 | if (pointer_actions[i] != ACTION_NONE) { 65 | window_grab_button(win, BUTTONS[i], pointer_modifier); 66 | } 67 | } 68 | } 69 | 70 | void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier) 71 | { 72 | #define GRAB(b, m) \ 73 | xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \ 74 | XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m) 75 | GRAB(button, modifier); 76 | if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) { 77 | GRAB(button, modifier | num_lock | caps_lock | scroll_lock); 78 | } 79 | if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) { 80 | GRAB(button, modifier | num_lock | caps_lock); 81 | } 82 | if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) { 83 | GRAB(button, modifier | caps_lock | scroll_lock); 84 | } 85 | if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) { 86 | GRAB(button, modifier | num_lock | scroll_lock); 87 | } 88 | if (num_lock != XCB_NO_SYMBOL) { 89 | GRAB(button, modifier | num_lock); 90 | } 91 | if (caps_lock != XCB_NO_SYMBOL) { 92 | GRAB(button, modifier | caps_lock); 93 | } 94 | if (scroll_lock != XCB_NO_SYMBOL) { 95 | GRAB(button, modifier | scroll_lock); 96 | } 97 | #undef GRAB 98 | } 99 | 100 | void grab_buttons(void) 101 | { 102 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 103 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 104 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { 105 | window_grab_buttons(n->id); 106 | if (n->presel != NULL) { 107 | window_grab_buttons(n->presel->feedback); 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | void ungrab_buttons(void) 115 | { 116 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 117 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 118 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { 119 | xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY); 120 | } 121 | } 122 | } 123 | } 124 | 125 | int16_t modfield_from_keysym(xcb_keysym_t keysym) 126 | { 127 | uint16_t modfield = 0; 128 | xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL; 129 | xcb_get_modifier_mapping_reply_t *reply = NULL; 130 | xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy); 131 | 132 | if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL || 133 | (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL || 134 | reply->keycodes_per_modifier < 1 || 135 | (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) { 136 | goto end; 137 | } 138 | 139 | unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier; 140 | for (unsigned int i = 0; i < num_mod; i++) { 141 | for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) { 142 | xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j]; 143 | if (mk == XCB_NO_SYMBOL) { 144 | continue; 145 | } 146 | for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) { 147 | if (*k == mk) { 148 | modfield |= (1 << i); 149 | } 150 | } 151 | } 152 | } 153 | 154 | end: 155 | xcb_key_symbols_free(symbols); 156 | free(keycodes); 157 | free(reply); 158 | return modfield; 159 | } 160 | 161 | resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac) 162 | { 163 | resize_handle_t rh = HANDLE_BOTTOM_RIGHT; 164 | xcb_rectangle_t rect = get_rectangle(NULL, NULL, n); 165 | if (pac == ACTION_RESIZE_SIDE) { 166 | float W = rect.width; 167 | float H = rect.height; 168 | float ratio = W / H; 169 | float x = pos.x - rect.x; 170 | float y = pos.y - rect.y; 171 | float diag_a = ratio * y; 172 | float diag_b = W - diag_a; 173 | if (x < diag_a) { 174 | if (x < diag_b) { 175 | rh = HANDLE_LEFT; 176 | } else { 177 | rh = HANDLE_BOTTOM; 178 | } 179 | } else { 180 | if (x < diag_b) { 181 | rh = HANDLE_TOP; 182 | } else { 183 | rh = HANDLE_RIGHT; 184 | } 185 | } 186 | } else if (pac == ACTION_RESIZE_CORNER) { 187 | int16_t mid_x = rect.x + (rect.width / 2); 188 | int16_t mid_y = rect.y + (rect.height / 2); 189 | if (pos.x > mid_x) { 190 | if (pos.y > mid_y) { 191 | rh = HANDLE_BOTTOM_RIGHT; 192 | } else { 193 | rh = HANDLE_TOP_RIGHT; 194 | } 195 | } else { 196 | if (pos.y > mid_y) { 197 | rh = HANDLE_BOTTOM_LEFT; 198 | } else { 199 | rh = HANDLE_TOP_LEFT; 200 | } 201 | } 202 | } 203 | return rh; 204 | } 205 | 206 | bool grab_pointer(pointer_action_t pac) 207 | { 208 | xcb_window_t win = XCB_NONE; 209 | xcb_point_t pos; 210 | 211 | query_pointer(&win, &pos); 212 | 213 | coordinates_t loc; 214 | 215 | if (!locate_window(win, &loc)) { 216 | if (pac == ACTION_FOCUS) { 217 | monitor_t *m = monitor_from_point(pos); 218 | if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) { 219 | focus_node(m, m->desk, m->desk->focus); 220 | return true; 221 | } 222 | } 223 | return false; 224 | } 225 | 226 | if (pac == ACTION_FOCUS) { 227 | if (loc.node != mon->desk->focus) { 228 | focus_node(loc.monitor, loc.desktop, loc.node); 229 | return true; 230 | } else if (focus_follows_pointer) { 231 | stack(loc.desktop, loc.node, true); 232 | } 233 | return false; 234 | } 235 | 236 | if (loc.node->client->state == STATE_FULLSCREEN) { 237 | return true; 238 | } 239 | 240 | xcb_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); 241 | 242 | if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) { 243 | free(reply); 244 | return true; 245 | } 246 | free(reply); 247 | 248 | if (pac == ACTION_MOVE) { 249 | put_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); 250 | } else if (pac == ACTION_RESIZE_CORNER) { 251 | put_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); 252 | } else if (pac == ACTION_RESIZE_SIDE) { 253 | put_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); 254 | } 255 | 256 | track_pointer(loc, pac, pos); 257 | 258 | return true; 259 | } 260 | 261 | void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos) 262 | { 263 | node_t *n = loc.node; 264 | resize_handle_t rh = get_handle(loc.node, pos, pac); 265 | 266 | uint16_t last_motion_x = pos.x, last_motion_y = pos.y; 267 | xcb_timestamp_t last_motion_time = 0; 268 | 269 | xcb_generic_event_t *evt = NULL; 270 | 271 | grabbing = true; 272 | grabbed_node = n; 273 | 274 | do { 275 | free(evt); 276 | while ((evt = xcb_wait_for_event(dpy)) == NULL) { 277 | xcb_flush(dpy); 278 | } 279 | uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt); 280 | if (resp_type == XCB_MOTION_NOTIFY) { 281 | xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt; 282 | uint32_t dtime = e->time - last_motion_time; 283 | if (dtime < pointer_motion_interval) { 284 | continue; 285 | } 286 | last_motion_time = e->time; 287 | int16_t dx = e->root_x - last_motion_x; 288 | int16_t dy = e->root_y - last_motion_y; 289 | if (pac == ACTION_MOVE) { 290 | move_client(&loc, dx, dy); 291 | } else if (n) { 292 | client_t *c = n->client; 293 | if (c && SHOULD_HONOR_SIZE_HINTS(c->honor_size_hints, c->state)) { 294 | resize_client(&loc, rh, e->root_x, e->root_y, false); 295 | } else { 296 | resize_client(&loc, rh, dx, dy, true); 297 | } 298 | } 299 | last_motion_x = e->root_x; 300 | last_motion_y = e->root_y; 301 | xcb_flush(dpy); 302 | } else if (resp_type == XCB_BUTTON_RELEASE) { 303 | grabbing = false; 304 | } else { 305 | handle_event(evt); 306 | } 307 | } while (grabbing && grabbed_node != NULL); 308 | free(evt); 309 | 310 | xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME); 311 | 312 | if (grabbed_node == NULL) { 313 | grabbing = false; 314 | return; 315 | } 316 | 317 | if (pac == ACTION_MOVE) { 318 | put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id); 319 | } else if (pac == ACTION_RESIZE_CORNER) { 320 | put_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); 321 | } else if (pac == ACTION_RESIZE_SIDE) { 322 | put_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); 323 | } 324 | 325 | xcb_rectangle_t r = get_rectangle(NULL, NULL, n); 326 | 327 | put_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); 328 | 329 | if ((pac == ACTION_MOVE && IS_TILED(n->client)) || 330 | ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) && 331 | n->client->state == STATE_TILED)) { 332 | for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) { 333 | if (f == n || f->client == NULL || !IS_TILED(f->client)) { 334 | continue; 335 | } 336 | xcb_rectangle_t r = f->client->tiled_rectangle; 337 | put_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); 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /src/pointer.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_POINTER_H 26 | #define BSPWM_POINTER_H 27 | 28 | #define XK_Num_Lock 0xff7f 29 | #define XK_Caps_Lock 0xffe5 30 | #define XK_Scroll_Lock 0xff14 31 | 32 | extern uint16_t num_lock; 33 | extern uint16_t caps_lock; 34 | extern uint16_t scroll_lock; 35 | 36 | extern bool grabbing; 37 | extern node_t *grabbed_node; 38 | 39 | void pointer_init(void); 40 | void window_grab_buttons(xcb_window_t win); 41 | void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier); 42 | void grab_buttons(void); 43 | void ungrab_buttons(void); 44 | int16_t modfield_from_keysym(xcb_keysym_t keysym); 45 | resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac); 46 | bool grab_pointer(pointer_action_t pac); 47 | void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/query.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_QUERY_H 26 | #define BSPWM_QUERY_H 27 | 28 | #define PTH_TOK "/" 29 | 30 | typedef enum { 31 | DOMAIN_TREE, 32 | DOMAIN_MONITOR, 33 | DOMAIN_DESKTOP, 34 | DOMAIN_NODE 35 | } domain_t; 36 | 37 | enum { 38 | SELECTOR_OK, 39 | SELECTOR_INVALID, 40 | SELECTOR_BAD_MODIFIERS, 41 | SELECTOR_BAD_DESCRIPTOR 42 | }; 43 | 44 | typedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp); 45 | typedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp); 46 | 47 | void query_state(FILE *rsp); 48 | void query_monitor(monitor_t *m, FILE *rsp); 49 | void query_desktop(desktop_t *d, FILE *rsp); 50 | void query_node(node_t *n, FILE *rsp); 51 | void query_presel(presel_t *p, FILE *rsp); 52 | void query_client(client_t *c, FILE *rsp); 53 | void query_rectangle(xcb_rectangle_t r, FILE *rsp); 54 | void query_constraints(constraints_t c, FILE *rsp); 55 | void query_padding(padding_t p, FILE *rsp); 56 | void query_history(FILE *rsp); 57 | void query_coordinates(coordinates_t *loc, FILE *rsp); 58 | void query_stack(FILE *rsp); 59 | void query_subscribers(FILE *rsp); 60 | int 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); 61 | int 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); 62 | int 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); 63 | int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp); 64 | void fprint_monitor_id(monitor_t *m, FILE *rsp); 65 | void fprint_monitor_name(monitor_t *m, FILE *rsp); 66 | void fprint_desktop_id(desktop_t *d, FILE *rsp); 67 | void fprint_desktop_name(desktop_t *d, FILE *rsp); 68 | void print_ignore_request(state_transition_t st, FILE *rsp); 69 | void print_modifier_mask(uint16_t m, FILE *rsp); 70 | void print_button_index(int8_t b, FILE *rsp); 71 | void print_pointer_action(pointer_action_t a, FILE *rsp); 72 | void resolve_rule_consequence(rule_consequence_t *csq); 73 | void print_rule_consequence(char **buf, rule_consequence_t *csq); 74 | void print_rectangle(char **buf, xcb_rectangle_t *rect); 75 | node_select_t make_node_select(void); 76 | desktop_select_t make_desktop_select(void); 77 | monitor_select_t make_monitor_select(void); 78 | int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst); 79 | int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst); 80 | int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst); 81 | bool locate_leaf(xcb_window_t win, coordinates_t *loc); 82 | bool locate_window(xcb_window_t win, coordinates_t *loc); 83 | bool locate_desktop(char *name, coordinates_t *loc); 84 | bool locate_monitor(char *name, coordinates_t *loc); 85 | bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm); 86 | bool desktop_from_name(char *name, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel, int *hits); 87 | bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm); 88 | bool monitor_from_id(uint32_t id, coordinates_t *loc); 89 | bool monitor_from_index(int idx, coordinates_t *loc); 90 | bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t *sel); 91 | bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t *sel); 92 | bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t *sel); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/restore.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_RESTORE_H 26 | #define BSPWM_RESTORE_H 27 | 28 | #include "jsmn.h" 29 | 30 | bool restore_state(const char *file_path); 31 | monitor_t *restore_monitor(jsmntok_t **t, char *json); 32 | desktop_t *restore_desktop(jsmntok_t **t, char *json); 33 | node_t *restore_node(jsmntok_t **t, char *json); 34 | presel_t *restore_presel(jsmntok_t **t, char *json); 35 | client_t *restore_client(jsmntok_t **t, char *json); 36 | void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json); 37 | void restore_constraints(constraints_t *c, jsmntok_t **t, char *json); 38 | void restore_padding(padding_t *p, jsmntok_t **t, char *json); 39 | void restore_history(jsmntok_t **t, char *json); 40 | void restore_subscribers(jsmntok_t **t, char *json); 41 | void restore_subscriber(subscriber_list_t *s, jsmntok_t **t, char *json); 42 | void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json); 43 | void restore_stack(jsmntok_t **t, char *json); 44 | bool keyeq(char *s, jsmntok_t *key, char *json); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/rule.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_RULE_H 26 | #define BSPWM_RULE_H 27 | 28 | #define MATCH_ANY "*" 29 | #define CSQ_BLK " =,\n" 30 | 31 | rule_t *make_rule(void); 32 | void add_rule(rule_t *r); 33 | void remove_rule(rule_t *r); 34 | void remove_rule_by_cause(char *cause); 35 | bool remove_rule_by_index(int idx); 36 | rule_consequence_t *make_rule_consequence(void); 37 | pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq); 38 | void add_pending_rule(pending_rule_t *pr); 39 | void remove_pending_rule(pending_rule_t *pr); 40 | void postpone_event(pending_rule_t *pr, xcb_generic_event_t *evt); 41 | event_queue_t *make_event_queue(xcb_generic_event_t *evt); 42 | void _apply_window_type(xcb_window_t win, rule_consequence_t *csq); 43 | void _apply_window_state(xcb_window_t win, rule_consequence_t *csq); 44 | void _apply_transient(xcb_window_t win, rule_consequence_t *csq); 45 | void _apply_hints(xcb_window_t win, rule_consequence_t *csq); 46 | void _apply_class(xcb_window_t win, rule_consequence_t *csq); 47 | void _apply_name(xcb_window_t win, rule_consequence_t *csq); 48 | void parse_keys_values(char *buf, rule_consequence_t *csq); 49 | void apply_rules(xcb_window_t win, rule_consequence_t *csq); 50 | bool schedule_rules(xcb_window_t win, rule_consequence_t *csq); 51 | void parse_rule_consequence(int fd, rule_consequence_t *csq); 52 | void parse_key_value(char *key, char *value, rule_consequence_t *csq); 53 | void list_rules(FILE *rsp); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/settings.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include "bspwm.h" 29 | #include "settings.h" 30 | 31 | char external_rules_command[MAXLEN]; 32 | char status_prefix[MAXLEN]; 33 | 34 | char normal_border_color[MAXLEN]; 35 | char active_border_color[MAXLEN]; 36 | char focused_border_color[MAXLEN]; 37 | char presel_feedback_color[MAXLEN]; 38 | 39 | padding_t padding; 40 | padding_t monocle_padding; 41 | int window_gap; 42 | unsigned int border_width; 43 | double split_ratio; 44 | child_polarity_t initial_polarity; 45 | automatic_scheme_t automatic_scheme; 46 | bool removal_adjustment; 47 | tightness_t directional_focus_tightness; 48 | 49 | uint16_t pointer_modifier; 50 | uint32_t pointer_motion_interval; 51 | pointer_action_t pointer_actions[3]; 52 | int8_t mapping_events_count; 53 | 54 | bool presel_feedback; 55 | bool borderless_monocle; 56 | bool gapless_monocle; 57 | bool single_monocle; 58 | bool borderless_singleton; 59 | 60 | bool focus_follows_pointer; 61 | bool pointer_follows_focus; 62 | bool pointer_follows_monitor; 63 | int8_t click_to_focus; 64 | bool swallow_first_click; 65 | bool ignore_ewmh_focus; 66 | bool ignore_ewmh_struts; 67 | state_transition_t ignore_ewmh_fullscreen; 68 | 69 | bool center_pseudo_tiled; 70 | honor_size_hints_mode_t honor_size_hints; 71 | 72 | bool remove_disabled_monitors; 73 | bool remove_unplugged_monitors; 74 | bool merge_overlapping_monitors; 75 | 76 | void run_config(int run_level) 77 | { 78 | if (fork() == 0) { 79 | if (dpy != NULL) { 80 | close(xcb_get_file_descriptor(dpy)); 81 | } 82 | setsid(); 83 | char arg1[2]; 84 | snprintf(arg1, 2, "%i", run_level); 85 | execl(config_path, config_path, arg1, (char *) NULL); 86 | err("Couldn't execute the configuration file.\n"); 87 | } 88 | } 89 | 90 | void load_settings(void) 91 | { 92 | snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND); 93 | snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX); 94 | 95 | snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR); 96 | snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR); 97 | snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR); 98 | snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR); 99 | 100 | padding = (padding_t) PADDING; 101 | monocle_padding = (padding_t) MONOCLE_PADDING; 102 | window_gap = WINDOW_GAP; 103 | border_width = BORDER_WIDTH; 104 | split_ratio = SPLIT_RATIO; 105 | initial_polarity = SECOND_CHILD; 106 | automatic_scheme = AUTOMATIC_SCHEME; 107 | removal_adjustment = REMOVAL_ADJUSTMENT; 108 | directional_focus_tightness = TIGHTNESS_HIGH; 109 | 110 | pointer_modifier = POINTER_MODIFIER; 111 | pointer_motion_interval = POINTER_MOTION_INTERVAL; 112 | pointer_actions[0] = ACTION_MOVE; 113 | pointer_actions[1] = ACTION_RESIZE_SIDE; 114 | pointer_actions[2] = ACTION_RESIZE_CORNER; 115 | mapping_events_count = MAPPING_EVENTS_COUNT; 116 | 117 | presel_feedback = PRESEL_FEEDBACK; 118 | borderless_monocle = BORDERLESS_MONOCLE; 119 | gapless_monocle = GAPLESS_MONOCLE; 120 | single_monocle = SINGLE_MONOCLE; 121 | borderless_singleton = BORDERLESS_SINGLETON; 122 | 123 | focus_follows_pointer = FOCUS_FOLLOWS_POINTER; 124 | pointer_follows_focus = POINTER_FOLLOWS_FOCUS; 125 | pointer_follows_monitor = POINTER_FOLLOWS_MONITOR; 126 | click_to_focus = CLICK_TO_FOCUS; 127 | swallow_first_click = SWALLOW_FIRST_CLICK; 128 | ignore_ewmh_focus = IGNORE_EWMH_FOCUS; 129 | ignore_ewmh_fullscreen = IGNORE_EWMH_FULLSCREEN; 130 | ignore_ewmh_struts = IGNORE_EWMH_STRUTS; 131 | 132 | center_pseudo_tiled = CENTER_PSEUDO_TILED; 133 | honor_size_hints = HONOR_SIZE_HINTS; 134 | 135 | remove_disabled_monitors = REMOVE_DISABLED_MONITORS; 136 | remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS; 137 | merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS; 138 | } 139 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_SETTINGS_H 26 | #define BSPWM_SETTINGS_H 27 | 28 | #include "types.h" 29 | 30 | #define POINTER_MODIFIER XCB_MOD_MASK_4 31 | #define POINTER_MOTION_INTERVAL 17 32 | #define EXTERNAL_RULES_COMMAND "" 33 | #define STATUS_PREFIX "W" 34 | 35 | #define NORMAL_BORDER_COLOR "#30302f" 36 | #define ACTIVE_BORDER_COLOR "#474645" 37 | #define FOCUSED_BORDER_COLOR "#817f7f" 38 | #define PRESEL_FEEDBACK_COLOR "#f4d775" 39 | 40 | #define PADDING {0, 0, 0, 0} 41 | #define MONOCLE_PADDING {0, 0, 0, 0} 42 | #define WINDOW_GAP 6 43 | #define BORDER_WIDTH 1 44 | #define SPLIT_RATIO 0.5 45 | #define AUTOMATIC_SCHEME SCHEME_LONGEST_SIDE 46 | #define REMOVAL_ADJUSTMENT true 47 | 48 | #define PRESEL_FEEDBACK true 49 | #define BORDERLESS_MONOCLE false 50 | #define GAPLESS_MONOCLE false 51 | #define SINGLE_MONOCLE false 52 | #define BORDERLESS_SINGLETON false 53 | 54 | #define FOCUS_FOLLOWS_POINTER false 55 | #define POINTER_FOLLOWS_FOCUS false 56 | #define POINTER_FOLLOWS_MONITOR false 57 | #define CLICK_TO_FOCUS XCB_BUTTON_INDEX_1 58 | #define SWALLOW_FIRST_CLICK false 59 | #define IGNORE_EWMH_FOCUS false 60 | #define IGNORE_EWMH_FULLSCREEN 0 61 | #define IGNORE_EWMH_STRUTS false 62 | 63 | #define CENTER_PSEUDO_TILED true 64 | #define HONOR_SIZE_HINTS HONOR_SIZE_HINTS_NO 65 | #define MAPPING_EVENTS_COUNT 1 66 | 67 | #define REMOVE_DISABLED_MONITORS false 68 | #define REMOVE_UNPLUGGED_MONITORS false 69 | #define MERGE_OVERLAPPING_MONITORS false 70 | 71 | extern char external_rules_command[MAXLEN]; 72 | extern char status_prefix[MAXLEN]; 73 | 74 | extern char normal_border_color[MAXLEN]; 75 | extern char active_border_color[MAXLEN]; 76 | extern char focused_border_color[MAXLEN]; 77 | extern char presel_feedback_color[MAXLEN]; 78 | 79 | extern padding_t padding; 80 | extern padding_t monocle_padding; 81 | extern int window_gap; 82 | extern unsigned int border_width; 83 | extern double split_ratio; 84 | extern child_polarity_t initial_polarity; 85 | extern automatic_scheme_t automatic_scheme; 86 | extern bool removal_adjustment; 87 | extern tightness_t directional_focus_tightness; 88 | 89 | extern uint16_t pointer_modifier; 90 | extern uint32_t pointer_motion_interval; 91 | extern pointer_action_t pointer_actions[3]; 92 | extern int8_t mapping_events_count; 93 | 94 | extern bool presel_feedback; 95 | extern bool borderless_monocle; 96 | extern bool gapless_monocle; 97 | extern bool single_monocle; 98 | extern bool borderless_singleton; 99 | 100 | extern bool focus_follows_pointer; 101 | extern bool pointer_follows_focus; 102 | extern bool pointer_follows_monitor; 103 | extern int8_t click_to_focus; 104 | extern bool swallow_first_click; 105 | extern bool ignore_ewmh_focus; 106 | extern bool ignore_ewmh_struts; 107 | extern state_transition_t ignore_ewmh_fullscreen; 108 | 109 | extern bool center_pseudo_tiled; 110 | extern honor_size_hints_mode_t honor_size_hints; 111 | 112 | extern bool remove_disabled_monitors; 113 | extern bool remove_unplugged_monitors; 114 | extern bool merge_overlapping_monitors; 115 | 116 | void run_config(int run_level); 117 | void load_settings(void); 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include "bspwm.h" 27 | #include "window.h" 28 | #include "subscribe.h" 29 | #include "ewmh.h" 30 | #include "tree.h" 31 | #include "stack.h" 32 | 33 | stacking_list_t *make_stack(node_t *n) 34 | { 35 | stacking_list_t *s = calloc(1, sizeof(stacking_list_t)); 36 | s->node = n; 37 | s->prev = s->next = NULL; 38 | return s; 39 | } 40 | 41 | void stack_insert_after(stacking_list_t *a, node_t *n) 42 | { 43 | stacking_list_t *s = make_stack(n); 44 | if (a == NULL) { 45 | stack_head = stack_tail = s; 46 | } else { 47 | if (a->node == n) { 48 | free(s); 49 | return; 50 | } 51 | remove_stack_node(n); 52 | stacking_list_t *b = a->next; 53 | if (b != NULL) { 54 | b->prev = s; 55 | } 56 | s->next = b; 57 | s->prev = a; 58 | a->next = s; 59 | if (stack_tail == a) { 60 | stack_tail = s; 61 | } 62 | } 63 | } 64 | 65 | void stack_insert_before(stacking_list_t *a, node_t *n) 66 | { 67 | stacking_list_t *s = make_stack(n); 68 | if (a == NULL) { 69 | stack_head = stack_tail = s; 70 | } else { 71 | if (a->node == n) { 72 | free(s); 73 | return; 74 | } 75 | remove_stack_node(n); 76 | stacking_list_t *b = a->prev; 77 | if (b != NULL) { 78 | b->next = s; 79 | } 80 | s->prev = b; 81 | s->next = a; 82 | a->prev = s; 83 | if (stack_head == a) { 84 | stack_head = s; 85 | } 86 | } 87 | } 88 | 89 | void remove_stack(stacking_list_t *s) 90 | { 91 | if (s == NULL) { 92 | return; 93 | } 94 | stacking_list_t *a = s->prev; 95 | stacking_list_t *b = s->next; 96 | if (a != NULL) { 97 | a->next = b; 98 | } 99 | if (b != NULL) { 100 | b->prev = a; 101 | } 102 | if (s == stack_head) { 103 | stack_head = b; 104 | } 105 | if (s == stack_tail) { 106 | stack_tail = a; 107 | } 108 | free(s); 109 | } 110 | 111 | void remove_stack_node(node_t *n) 112 | { 113 | for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) { 114 | for (stacking_list_t *s = stack_head; s != NULL; s = s->next) { 115 | if (s->node == f) { 116 | remove_stack(s); 117 | break; 118 | } 119 | } 120 | } 121 | } 122 | 123 | int stack_level(client_t *c) 124 | { 125 | int layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2)); 126 | int state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 1 : 2)); 127 | return 3 * layer_level + state_level; 128 | } 129 | 130 | int stack_cmp(client_t *c1, client_t *c2) 131 | { 132 | return stack_level(c1) - stack_level(c2); 133 | } 134 | 135 | stacking_list_t *limit_above(node_t *n) 136 | { 137 | stacking_list_t *s = stack_head; 138 | while (s != NULL && stack_cmp(n->client, s->node->client) >= 0) { 139 | s = s->next; 140 | } 141 | if (s == NULL) { 142 | s = stack_tail; 143 | } 144 | if (s->node == n) { 145 | s = s->prev; 146 | } 147 | return s; 148 | } 149 | 150 | stacking_list_t *limit_below(node_t *n) 151 | { 152 | stacking_list_t *s = stack_tail; 153 | while (s != NULL && stack_cmp(n->client, s->node->client) <= 0) { 154 | s = s->prev; 155 | } 156 | if (s == NULL) { 157 | s = stack_head; 158 | } 159 | if (s->node == n) { 160 | s = s->next; 161 | } 162 | return s; 163 | } 164 | 165 | void stack(desktop_t *d, node_t *n, bool focused) 166 | { 167 | for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) { 168 | if (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) { 169 | continue; 170 | } 171 | 172 | if (stack_head == NULL) { 173 | stack_insert_after(NULL, f); 174 | } else { 175 | stacking_list_t *s = (focused ? limit_above(f) : limit_below(f)); 176 | if (s == NULL) { 177 | continue; 178 | } 179 | int i = stack_cmp(f->client, s->node->client); 180 | if (i < 0 || (i == 0 && !focused)) { 181 | stack_insert_before(s, f); 182 | window_below(f->id, s->node->id); 183 | put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X below 0x%08X\n", f->id, s->node->id); 184 | } else { 185 | stack_insert_after(s, f); 186 | window_above(f->id, s->node->id); 187 | put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X above 0x%08X\n", f->id, s->node->id); 188 | } 189 | } 190 | } 191 | 192 | ewmh_update_client_list(true); 193 | restack_presel_feedbacks(d); 194 | } 195 | 196 | void restack_presel_feedbacks(desktop_t *d) 197 | { 198 | stacking_list_t *s = stack_tail; 199 | while (s != NULL && !IS_TILED(s->node->client)) { 200 | s = s->prev; 201 | } 202 | if (s != NULL) { 203 | restack_presel_feedbacks_in(d->root, s->node); 204 | } 205 | } 206 | 207 | void restack_presel_feedbacks_in(node_t *r, node_t *n) 208 | { 209 | if (r == NULL) { 210 | return; 211 | } else { 212 | if (r->presel != NULL) { 213 | window_above(r->presel->feedback, n->id); 214 | } 215 | restack_presel_feedbacks_in(r->first_child, n); 216 | restack_presel_feedbacks_in(r->second_child, n); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/stack.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_STACK_H 26 | #define BSPWM_STACK_H 27 | 28 | stacking_list_t *make_stack(node_t *n); 29 | void stack_insert_after(stacking_list_t *a, node_t *n); 30 | void stack_insert_before(stacking_list_t *a, node_t *n); 31 | void remove_stack(stacking_list_t *s); 32 | void remove_stack_node(node_t *n); 33 | int stack_level(client_t *c); 34 | int stack_cmp(client_t *c1, client_t *c2); 35 | stacking_list_t *limit_above(node_t *n); 36 | stacking_list_t *limit_below(node_t *n); 37 | void stack(desktop_t *d, node_t *n, bool focused); 38 | void restack_presel_feedbacks(desktop_t *d); 39 | void restack_presel_feedbacks_in(node_t *r, node_t *n); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/subscribe.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "bspwm.h" 32 | #include "desktop.h" 33 | #include "settings.h" 34 | #include "subscribe.h" 35 | #include "tree.h" 36 | 37 | subscriber_list_t *make_subscriber(FILE *stream, char *fifo_path, int field, int count) 38 | { 39 | subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t)); 40 | sb->prev = sb->next = NULL; 41 | sb->stream = stream; 42 | sb->fifo_path = fifo_path; 43 | sb->field = field; 44 | sb->count = count; 45 | return sb; 46 | } 47 | 48 | void remove_subscriber(subscriber_list_t *sb) 49 | { 50 | if (sb == NULL) { 51 | return; 52 | } 53 | subscriber_list_t *a = sb->prev; 54 | subscriber_list_t *b = sb->next; 55 | if (a != NULL) { 56 | a->next = b; 57 | } 58 | if (b != NULL) { 59 | b->prev = a; 60 | } 61 | if (sb == subscribe_head) { 62 | subscribe_head = b; 63 | } 64 | if (sb == subscribe_tail) { 65 | subscribe_tail = a; 66 | } 67 | if (restart) { 68 | int cli_fd = fileno(sb->stream); 69 | fcntl(cli_fd, F_SETFD, ~FD_CLOEXEC & fcntl(cli_fd, F_GETFD)); 70 | } else { 71 | fclose(sb->stream); 72 | unlink(sb->fifo_path); 73 | } 74 | free(sb->fifo_path); 75 | free(sb); 76 | } 77 | 78 | void add_subscriber(subscriber_list_t *sb) 79 | { 80 | if (subscribe_head == NULL) { 81 | subscribe_head = subscribe_tail = sb; 82 | } else { 83 | subscribe_tail->next = sb; 84 | sb->prev = subscribe_tail; 85 | subscribe_tail = sb; 86 | } 87 | int cli_fd = fileno(sb->stream); 88 | fcntl(cli_fd, F_SETFD, FD_CLOEXEC | fcntl(cli_fd, F_GETFD)); 89 | if (sb->field & SBSC_MASK_REPORT) { 90 | print_report(sb->stream); 91 | if (sb->count-- == 1) { 92 | remove_subscriber(sb); 93 | } 94 | } 95 | } 96 | 97 | int print_report(FILE *stream) 98 | { 99 | fprintf(stream, "%s", status_prefix); 100 | for (monitor_t *m = mon_head; m != NULL; m = m->next) { 101 | fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name); 102 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { 103 | char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o')); 104 | if (m->desk == d) { 105 | c = toupper(c); 106 | } 107 | fprintf(stream, ":%c%s", c, d->name); 108 | } 109 | if (m->desk != NULL) { 110 | fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout)); 111 | if (m->desk->focus != NULL) { 112 | node_t *n = m->desk->focus; 113 | if (n->client != NULL) { 114 | fprintf(stream, ":T%c", STATE_CHR(n->client->state)); 115 | } else { 116 | fprintf(stream, ":T@"); 117 | } 118 | int i = 0; 119 | char flags[5]; 120 | if (n->sticky) { 121 | flags[i++] = 'S'; 122 | } 123 | if (n->private) { 124 | flags[i++] = 'P'; 125 | } 126 | if (n->locked) { 127 | flags[i++] = 'L'; 128 | } 129 | if (n->marked) { 130 | flags[i++] = 'M'; 131 | } 132 | flags[i] = '\0'; 133 | fprintf(stream, ":G%s", flags); 134 | } 135 | } 136 | if (m != mon_tail) { 137 | fprintf(stream, "%s", ":"); 138 | } 139 | } 140 | fprintf(stream, "%s", "\n"); 141 | return fflush(stream); 142 | } 143 | 144 | void put_status(subscriber_mask_t mask, ...) 145 | { 146 | subscriber_list_t *sb = subscribe_head; 147 | int ret; 148 | while (sb != NULL) { 149 | subscriber_list_t *next = sb->next; 150 | if (sb->field & mask) { 151 | if (sb->count > 0) { 152 | sb->count--; 153 | } 154 | if (mask == SBSC_MASK_REPORT) { 155 | ret = print_report(sb->stream); 156 | } else { 157 | char *fmt; 158 | va_list args; 159 | va_start(args, mask); 160 | fmt = va_arg(args, char *); 161 | vfprintf(sb->stream, fmt, args); 162 | va_end(args); 163 | ret = fflush(sb->stream); 164 | } 165 | if (ret != 0 || sb->count == 0) { 166 | remove_subscriber(sb); 167 | } 168 | } 169 | sb = next; 170 | } 171 | } 172 | 173 | void prune_dead_subscribers(void) 174 | { 175 | subscriber_list_t *sb = subscribe_head; 176 | while (sb != NULL) { 177 | subscriber_list_t *next = sb->next; 178 | // Is the subscriber's stream closed? 179 | if (write(fileno(sb->stream), NULL, 0) == -1) { 180 | remove_subscriber(sb); 181 | } 182 | sb = next; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/subscribe.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_SUBSCRIBE_H 26 | #define BSPWM_SUBSCRIBE_H 27 | 28 | #define FIFO_TEMPLATE "bspwm_fifo.XXXXXX" 29 | 30 | typedef enum { 31 | SBSC_MASK_REPORT = 1 << 0, 32 | SBSC_MASK_MONITOR_ADD = 1 << 1, 33 | SBSC_MASK_MONITOR_RENAME = 1 << 2, 34 | SBSC_MASK_MONITOR_REMOVE = 1 << 3, 35 | SBSC_MASK_MONITOR_SWAP = 1 << 4, 36 | SBSC_MASK_MONITOR_FOCUS = 1 << 5, 37 | SBSC_MASK_MONITOR_GEOMETRY = 1 << 6, 38 | SBSC_MASK_DESKTOP_ADD = 1 << 7, 39 | SBSC_MASK_DESKTOP_RENAME = 1 << 8, 40 | SBSC_MASK_DESKTOP_REMOVE = 1 << 9, 41 | SBSC_MASK_DESKTOP_SWAP = 1 << 10, 42 | SBSC_MASK_DESKTOP_TRANSFER = 1 << 11, 43 | SBSC_MASK_DESKTOP_FOCUS = 1 << 12, 44 | SBSC_MASK_DESKTOP_ACTIVATE = 1 << 13, 45 | SBSC_MASK_DESKTOP_LAYOUT = 1 << 14, 46 | SBSC_MASK_NODE_ADD = 1 << 15, 47 | SBSC_MASK_NODE_REMOVE = 1 << 16, 48 | SBSC_MASK_NODE_SWAP = 1 << 17, 49 | SBSC_MASK_NODE_TRANSFER = 1 << 18, 50 | SBSC_MASK_NODE_FOCUS = 1 << 19, 51 | SBSC_MASK_NODE_PRESEL = 1 << 20, 52 | SBSC_MASK_NODE_STACK = 1 << 21, 53 | SBSC_MASK_NODE_ACTIVATE = 1 << 22, 54 | SBSC_MASK_NODE_GEOMETRY = 1 << 23, 55 | SBSC_MASK_NODE_STATE = 1 << 24, 56 | SBSC_MASK_NODE_FLAG = 1 << 25, 57 | SBSC_MASK_NODE_LAYER = 1 << 26, 58 | SBSC_MASK_POINTER_ACTION = 1 << 27, 59 | SBSC_MASK_MONITOR = (1 << 7) - (1 << 1), 60 | SBSC_MASK_DESKTOP = (1 << 15) - (1 << 7), 61 | SBSC_MASK_NODE = (1 << 27) - (1 << 15), 62 | SBSC_MASK_ALL = (1 << 28) - 1 63 | } subscriber_mask_t; 64 | 65 | subscriber_list_t *make_subscriber(FILE *stream, char *fifo_path, int field, int count); 66 | void remove_subscriber(subscriber_list_t *sb); 67 | void add_subscriber(subscriber_list_t *sb); 68 | int print_report(FILE *stream); 69 | void put_status(subscriber_mask_t mask, ...); 70 | 71 | /* Remove any subscriber for which the stream has been closed and is no longer 72 | * writable. */ 73 | void prune_dead_subscribers(void); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/tree.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_TREE_H 26 | #define BSPWM_TREE_H 27 | 28 | #define MIN_WIDTH 32 29 | #define MIN_HEIGHT 32 30 | 31 | void arrange(monitor_t *m, desktop_t *d); 32 | void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect); 33 | presel_t *make_presel(void); 34 | bool set_type(node_t *n, split_type_t typ); 35 | bool set_ratio(node_t *n, double rat); 36 | void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir); 37 | void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio); 38 | void cancel_presel(monitor_t *m, desktop_t *d, node_t *n); 39 | void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n); 40 | node_t *find_public(desktop_t *d); 41 | node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f); 42 | void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n); 43 | bool activate_node(monitor_t *m, desktop_t *d, node_t *n); 44 | void transfer_sticky_nodes(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd, node_t *n); 45 | bool focus_node(monitor_t *m, desktop_t *d, node_t *n); 46 | void hide_node(desktop_t *d, node_t *n); 47 | void show_node(desktop_t *d, node_t *n); 48 | node_t *make_node(uint32_t id); 49 | client_t *make_client(void); 50 | void initialize_client(node_t *n); 51 | bool is_focusable(node_t *n); 52 | bool is_leaf(node_t *n); 53 | bool is_first_child(node_t *n); 54 | bool is_second_child(node_t *n); 55 | unsigned int clients_count_in(node_t *n); 56 | node_t *brother_tree(node_t *n); 57 | node_t *first_extrema(node_t *n); 58 | node_t *second_extrema(node_t *n); 59 | node_t *first_focusable_leaf(node_t *n); 60 | node_t *next_node(node_t *n); 61 | node_t *prev_node(node_t *n); 62 | node_t *next_leaf(node_t *n, node_t *r); 63 | node_t *prev_leaf(node_t *n, node_t *r); 64 | node_t *next_tiled_leaf(node_t *n, node_t *r); 65 | node_t *prev_tiled_leaf(node_t *n, node_t *r); 66 | bool is_adjacent(node_t *a, node_t *b, direction_t dir); 67 | node_t *find_fence(node_t *n, direction_t dir); 68 | bool is_child(node_t *a, node_t *b); 69 | bool is_descendant(node_t *a, node_t *b); 70 | bool find_by_id(uint32_t id, coordinates_t *loc); 71 | node_t *find_by_id_in(node_t *r, uint32_t id); 72 | void find_any_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 73 | bool find_any_node_in(monitor_t *m, desktop_t *d, node_t *n, coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 74 | void find_first_ancestor(coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 75 | void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t *sel); 76 | unsigned int node_area(desktop_t *d, node_t *n); 77 | int tiled_count(node_t *n, bool include_receptacles); 78 | void find_by_area(area_peak_t ap, coordinates_t *ref, coordinates_t *dst, node_select_t *sel); 79 | void rotate_tree(node_t *n, int deg); 80 | void rotate_tree_rec(node_t *n, int deg); 81 | void flip_tree(node_t *n, flip_t flp); 82 | void equalize_tree(node_t *n); 83 | int balance_tree(node_t *n); 84 | void adjust_ratios(node_t *n, xcb_rectangle_t rect); 85 | void unlink_node(monitor_t *m, desktop_t *d, node_t *n); 86 | void close_node(node_t *n); 87 | void kill_node(monitor_t *m, desktop_t *d, node_t *n); 88 | void remove_node(monitor_t *m, desktop_t *d, node_t *n); 89 | void free_node(node_t *n); 90 | bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2, bool follow); 91 | bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd, bool follow); 92 | bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t *sel); 93 | void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir); 94 | void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value); 95 | void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value); 96 | void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value); 97 | void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n); 98 | bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l); 99 | bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s); 100 | void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value); 101 | void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value); 102 | void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n); 103 | void rebuild_constraints_from_leaves(node_t *n); 104 | void rebuild_constraints_towards_root(node_t *n); 105 | void update_constraints(node_t *n); 106 | void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n); 107 | void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value); 108 | void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value); 109 | void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value); 110 | void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n); 111 | void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value); 112 | void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value); 113 | void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value); 114 | void set_marked(monitor_t *m, desktop_t *d, node_t *n, bool value); 115 | void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value); 116 | xcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n); 117 | void listen_enter_notify(node_t *n, bool enable); 118 | void regenerate_ids_in(node_t *n); 119 | 120 | unsigned int sticky_count(node_t *n); 121 | unsigned int private_count(node_t *n); 122 | unsigned int locked_count(node_t *n); 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_TYPES_H 26 | #define BSPWM_TYPES_H 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "helpers.h" 33 | 34 | #define MISSING_VALUE "N/A" 35 | #define MAX_WM_STATES 4 36 | 37 | typedef enum { 38 | TYPE_HORIZONTAL, 39 | TYPE_VERTICAL 40 | } split_type_t; 41 | 42 | typedef enum { 43 | MODE_AUTOMATIC, 44 | MODE_MANUAL 45 | } split_mode_t; 46 | 47 | typedef enum { 48 | SCHEME_LONGEST_SIDE, 49 | SCHEME_ALTERNATE, 50 | SCHEME_SPIRAL 51 | } automatic_scheme_t; 52 | 53 | typedef enum { 54 | HONOR_SIZE_HINTS_NO = 0, 55 | HONOR_SIZE_HINTS_YES, 56 | HONOR_SIZE_HINTS_FLOATING, 57 | HONOR_SIZE_HINTS_TILED, 58 | HONOR_SIZE_HINTS_DEFAULT 59 | } honor_size_hints_mode_t; 60 | 61 | typedef enum { 62 | STATE_TILED, 63 | STATE_PSEUDO_TILED, 64 | STATE_FLOATING, 65 | STATE_FULLSCREEN 66 | } client_state_t; 67 | 68 | typedef enum { 69 | WM_FLAG_MODAL = 1 << 0, 70 | WM_FLAG_STICKY = 1 << 1, 71 | WM_FLAG_MAXIMIZED_VERT = 1 << 2, 72 | WM_FLAG_MAXIMIZED_HORZ = 1 << 3, 73 | WM_FLAG_SHADED = 1 << 4, 74 | WM_FLAG_SKIP_TASKBAR = 1 << 5, 75 | WM_FLAG_SKIP_PAGER = 1 << 6, 76 | WM_FLAG_HIDDEN = 1 << 7, 77 | WM_FLAG_FULLSCREEN = 1 << 8, 78 | WM_FLAG_ABOVE = 1 << 9, 79 | WM_FLAG_BELOW = 1 << 10, 80 | WM_FLAG_DEMANDS_ATTENTION = 1 << 11, 81 | } wm_flags_t; 82 | 83 | typedef enum { 84 | LAYER_BELOW, 85 | LAYER_NORMAL, 86 | LAYER_ABOVE 87 | } stack_layer_t; 88 | 89 | typedef enum { 90 | OPTION_NONE, 91 | OPTION_TRUE, 92 | OPTION_FALSE 93 | } option_bool_t; 94 | 95 | typedef enum { 96 | ALTER_TOGGLE, 97 | ALTER_SET 98 | } alter_state_t; 99 | 100 | typedef enum { 101 | CYCLE_NEXT, 102 | CYCLE_PREV 103 | } cycle_dir_t; 104 | 105 | typedef enum { 106 | CIRCULATE_FORWARD, 107 | CIRCULATE_BACKWARD 108 | } circulate_dir_t; 109 | 110 | typedef enum { 111 | HISTORY_OLDER, 112 | HISTORY_NEWER 113 | } history_dir_t; 114 | 115 | typedef enum { 116 | DIR_NORTH, 117 | DIR_WEST, 118 | DIR_SOUTH, 119 | DIR_EAST 120 | } direction_t; 121 | 122 | typedef enum { 123 | HANDLE_LEFT = 1 << 0, 124 | HANDLE_TOP = 1 << 1, 125 | HANDLE_RIGHT = 1 << 2, 126 | HANDLE_BOTTOM = 1 << 3, 127 | HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT, 128 | HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT, 129 | HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT, 130 | HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT 131 | } resize_handle_t; 132 | 133 | typedef enum { 134 | ACTION_NONE, 135 | ACTION_FOCUS, 136 | ACTION_MOVE, 137 | ACTION_RESIZE_SIDE, 138 | ACTION_RESIZE_CORNER 139 | } pointer_action_t; 140 | 141 | typedef enum { 142 | LAYOUT_TILED, 143 | LAYOUT_MONOCLE 144 | } layout_t; 145 | 146 | typedef enum { 147 | FLIP_HORIZONTAL, 148 | FLIP_VERTICAL 149 | } flip_t; 150 | 151 | typedef enum { 152 | FIRST_CHILD, 153 | SECOND_CHILD 154 | } child_polarity_t; 155 | 156 | typedef enum { 157 | TIGHTNESS_LOW, 158 | TIGHTNESS_HIGH, 159 | } tightness_t; 160 | 161 | typedef enum { 162 | AREA_BIGGEST, 163 | AREA_SMALLEST, 164 | } area_peak_t; 165 | 166 | typedef enum { 167 | STATE_TRANSITION_ENTER = 1 << 0, 168 | STATE_TRANSITION_EXIT = 1 << 1, 169 | } state_transition_t; 170 | 171 | typedef struct { 172 | option_bool_t automatic; 173 | option_bool_t focused; 174 | option_bool_t active; 175 | option_bool_t local; 176 | option_bool_t leaf; 177 | option_bool_t window; 178 | option_bool_t tiled; 179 | option_bool_t pseudo_tiled; 180 | option_bool_t floating; 181 | option_bool_t fullscreen; 182 | option_bool_t hidden; 183 | option_bool_t sticky; 184 | option_bool_t private; 185 | option_bool_t locked; 186 | option_bool_t marked; 187 | option_bool_t urgent; 188 | option_bool_t same_class; 189 | option_bool_t descendant_of; 190 | option_bool_t ancestor_of; 191 | option_bool_t below; 192 | option_bool_t normal; 193 | option_bool_t above; 194 | option_bool_t horizontal; 195 | option_bool_t vertical; 196 | } node_select_t; 197 | 198 | typedef struct { 199 | option_bool_t occupied; 200 | option_bool_t focused; 201 | option_bool_t active; 202 | option_bool_t urgent; 203 | option_bool_t local; 204 | option_bool_t tiled; 205 | option_bool_t monocle; 206 | option_bool_t user_tiled; 207 | option_bool_t user_monocle; 208 | } desktop_select_t; 209 | 210 | typedef struct { 211 | option_bool_t occupied; 212 | option_bool_t focused; 213 | } monitor_select_t; 214 | 215 | typedef struct icccm_props_t icccm_props_t; 216 | struct icccm_props_t { 217 | bool take_focus; 218 | bool input_hint; 219 | bool delete_window; 220 | }; 221 | 222 | typedef struct { 223 | char class_name[MAXLEN]; 224 | char instance_name[MAXLEN]; 225 | char name[MAXLEN]; 226 | unsigned int border_width; 227 | bool urgent; 228 | bool shown; 229 | client_state_t state; 230 | client_state_t last_state; 231 | stack_layer_t layer; 232 | stack_layer_t last_layer; 233 | xcb_rectangle_t floating_rectangle; 234 | xcb_rectangle_t tiled_rectangle; 235 | honor_size_hints_mode_t honor_size_hints; 236 | xcb_size_hints_t size_hints; 237 | icccm_props_t icccm_props; 238 | wm_flags_t wm_flags; 239 | } client_t; 240 | 241 | typedef struct presel_t presel_t; 242 | struct presel_t { 243 | double split_ratio; 244 | direction_t split_dir; 245 | xcb_window_t feedback; 246 | }; 247 | 248 | typedef struct constraints_t constraints_t; 249 | struct constraints_t { 250 | uint16_t min_width; 251 | uint16_t min_height; 252 | }; 253 | 254 | typedef struct node_t node_t; 255 | struct node_t { 256 | uint32_t id; 257 | split_type_t split_type; 258 | double split_ratio; 259 | presel_t *presel; 260 | xcb_rectangle_t rectangle; 261 | constraints_t constraints; 262 | bool vacant; 263 | bool hidden; 264 | bool sticky; 265 | bool private; 266 | bool locked; 267 | bool marked; 268 | node_t *first_child; 269 | node_t *second_child; 270 | node_t *parent; 271 | client_t *client; 272 | }; 273 | 274 | typedef struct padding_t padding_t; 275 | struct padding_t { 276 | int top; 277 | int right; 278 | int bottom; 279 | int left; 280 | }; 281 | 282 | typedef struct desktop_t desktop_t; 283 | struct desktop_t { 284 | char name[SMALEN]; 285 | uint32_t id; 286 | layout_t layout; 287 | layout_t user_layout; 288 | node_t *root; 289 | node_t *focus; 290 | desktop_t *prev; 291 | desktop_t *next; 292 | padding_t padding; 293 | int window_gap; 294 | unsigned int border_width; 295 | }; 296 | 297 | typedef struct monitor_t monitor_t; 298 | struct monitor_t { 299 | char name[SMALEN]; 300 | uint32_t id; 301 | xcb_randr_output_t randr_id; 302 | xcb_window_t root; 303 | bool wired; 304 | padding_t padding; 305 | unsigned int sticky_count; 306 | int window_gap; 307 | unsigned int border_width; 308 | xcb_rectangle_t rectangle; 309 | desktop_t *desk; 310 | desktop_t *desk_head; 311 | desktop_t *desk_tail; 312 | monitor_t *prev; 313 | monitor_t *next; 314 | }; 315 | 316 | typedef struct { 317 | monitor_t *monitor; 318 | desktop_t *desktop; 319 | node_t *node; 320 | } coordinates_t; 321 | 322 | typedef struct history_t history_t; 323 | struct history_t { 324 | coordinates_t loc; 325 | bool latest; 326 | history_t *prev; 327 | history_t *next; 328 | }; 329 | 330 | typedef struct stacking_list_t stacking_list_t; 331 | struct stacking_list_t { 332 | node_t *node; 333 | stacking_list_t *prev; 334 | stacking_list_t *next; 335 | }; 336 | 337 | typedef struct event_queue_t event_queue_t; 338 | struct event_queue_t { 339 | xcb_generic_event_t event; 340 | event_queue_t *prev; 341 | event_queue_t *next; 342 | }; 343 | 344 | typedef struct subscriber_list_t subscriber_list_t; 345 | struct subscriber_list_t { 346 | FILE *stream; 347 | char* fifo_path; 348 | int field; 349 | int count; 350 | subscriber_list_t *prev; 351 | subscriber_list_t *next; 352 | }; 353 | 354 | typedef struct rule_t rule_t; 355 | struct rule_t { 356 | char class_name[MAXLEN]; 357 | char instance_name[MAXLEN]; 358 | char name[MAXLEN]; 359 | char effect[MAXLEN]; 360 | bool one_shot; 361 | rule_t *prev; 362 | rule_t *next; 363 | }; 364 | 365 | typedef struct { 366 | char class_name[MAXLEN]; 367 | char instance_name[MAXLEN]; 368 | char name[MAXLEN]; 369 | char monitor_desc[MAXLEN]; 370 | char desktop_desc[MAXLEN]; 371 | char node_desc[MAXLEN]; 372 | direction_t *split_dir; 373 | double split_ratio; 374 | stack_layer_t *layer; 375 | client_state_t *state; 376 | honor_size_hints_mode_t honor_size_hints; 377 | bool hidden; 378 | bool sticky; 379 | bool private; 380 | bool locked; 381 | bool marked; 382 | bool center; 383 | bool follow; 384 | bool manage; 385 | bool focus; 386 | bool border; 387 | xcb_rectangle_t *rect; 388 | } rule_consequence_t; 389 | 390 | typedef struct pending_rule_t pending_rule_t; 391 | struct pending_rule_t { 392 | int fd; 393 | xcb_window_t win; 394 | rule_consequence_t *csq; 395 | event_queue_t *event_head; 396 | event_queue_t *event_tail; 397 | pending_rule_t *prev; 398 | pending_rule_t *next; 399 | }; 400 | 401 | #endif 402 | -------------------------------------------------------------------------------- /src/window.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012, Bastien Dejean 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef BSPWM_WINDOW_H 26 | #define BSPWM_WINDOW_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "types.h" 33 | 34 | void schedule_window(xcb_window_t win); 35 | bool manage_window(xcb_window_t win, rule_consequence_t *csq, int fd); 36 | void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state); 37 | void unmanage_window(xcb_window_t win); 38 | bool is_presel_window(xcb_window_t win); 39 | void initialize_presel_feedback(node_t *n); 40 | void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n); 41 | void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n); 42 | void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n); 43 | void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n); 44 | void update_colors(void); 45 | void update_colors_in(node_t *n, desktop_t *d, monitor_t *m); 46 | void draw_border(node_t *n, bool focused_node, bool focused_monitor); 47 | void window_draw_border(xcb_window_t win, uint32_t border_color_pxl); 48 | void adopt_orphans(void); 49 | uint32_t get_border_color(bool focused_node, bool focused_monitor); 50 | void initialize_floating_rectangle(node_t *n); 51 | xcb_rectangle_t get_window_rectangle(node_t *n); 52 | bool move_client(coordinates_t *loc, int dx, int dy); 53 | bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy, bool relative); 54 | void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height); 55 | void query_pointer(xcb_window_t *win, xcb_point_t *pt); 56 | void update_motion_recorder(void); 57 | void enable_motion_recorder(xcb_window_t win); 58 | void disable_motion_recorder(void); 59 | void window_border_width(xcb_window_t win, uint32_t bw); 60 | void window_move(xcb_window_t win, int16_t x, int16_t y); 61 | void window_resize(xcb_window_t win, uint16_t w, uint16_t h); 62 | void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h); 63 | void window_center(monitor_t *m, client_t *c); 64 | void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode); 65 | void window_above(xcb_window_t w1, xcb_window_t w2); 66 | void window_below(xcb_window_t w1, xcb_window_t w2); 67 | void window_lower(xcb_window_t win); 68 | void window_set_visibility(xcb_window_t win, bool visible); 69 | void window_hide(xcb_window_t win); 70 | void window_show(xcb_window_t win); 71 | void update_input_focus(void); 72 | void set_input_focus(node_t *n); 73 | void clear_input_focus(void); 74 | void center_pointer(xcb_rectangle_t r); 75 | void get_atom(char *name, xcb_atom_t *atom); 76 | void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value); 77 | void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value); 78 | bool window_exists(xcb_window_t win); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | OUT = test_window 2 | CFLAGS += -std=c99 -pedantic -Wall -Wextra 3 | LDLIBS = -lxcb -lxcb-icccm 4 | 5 | SRC = $(wildcard *.c) 6 | OBJ = $(SRC:.c=.o) 7 | 8 | all: $(OUT) 9 | 10 | clean: 11 | $(RM) $(OUT) $(OBJ) 12 | 13 | .PHONY: all clean 14 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | - Install *jshon*. 2 | - Run `make` once. 3 | - Run `./run`. 4 | -------------------------------------------------------------------------------- /tests/desktop/swap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc wm -a "TEST-SWAP-A" 1024x512+0+0 6 | bspc wm -a "TEST-SWAP-B" 1024x512+0+512 7 | 8 | bspc monitor -f "TEST-SWAP-A" 9 | window add 3 10 | 11 | bspc monitor -f "TEST-SWAP-B" 12 | window add 2 13 | 14 | nodes_a=$(bspc query -N -m "TEST-SWAP-A") 15 | nodes_b=$(bspc query -N -m "TEST-SWAP-B") 16 | 17 | bspc desktop "TEST-SWAP-A:^1" -s "TEST-SWAP-B:^1" 18 | 19 | [ "$(bspc query -N -m 'TEST-SWAP-A')" = "$nodes_b" ] || fail "Wrong nodes in first monitor" 20 | [ "$(bspc query -N -m 'TEST-SWAP-B')" = "$nodes_a" ] || fail "Wrong nodes in second monitor" 21 | 22 | window remove 3 23 | bspc monitor -f "TEST-SWAP-A" 24 | window remove 2 25 | 26 | bspc monitor "TEST-SWAP-A" -r 27 | bspc monitor "TEST-SWAP-B" -r 28 | -------------------------------------------------------------------------------- /tests/desktop/transfer: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc wm -a "TEST-TRANSFER-A" 1024x512+0+0 6 | bspc wm -a "TEST-TRANSFER-B" 1024x512+0+512 7 | 8 | bspc monitor "TEST-TRANSFER-A" -a source 9 | bspc monitor -f "TEST-TRANSFER-A" 10 | 11 | window add 3 12 | 13 | root_rectangle_y=$(bspc query -T -n @/ | jshon -e rectangle -e y) 14 | 15 | bspc desktop "TEST-TRANSFER-A:focused" -m "TEST-TRANSFER-B" 16 | 17 | [ "$(bspc query -D -m "TEST-TRANSFER-A" | wc -l)" -eq 1 ] || fail "Invalid number of desktop in source after transfer." 18 | 19 | bspc desktop "TEST-TRANSFER-B:^2" -f 20 | 21 | [ "$(bspc query -T -n @/ | jshon -e rectangle -e y)" -ne "$root_rectangle_y" ] || fail "Wrong tiled rectangle for root in destination." 22 | 23 | window remove 3 24 | 25 | bspc monitor "TEST-TRANSFER-A" -r 26 | bspc monitor "TEST-TRANSFER-B" -r 27 | -------------------------------------------------------------------------------- /tests/node/flags: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-sticky-a" 6 | bspc monitor -a "test-sticky-b" 7 | 8 | bspc desktop -f "test-sticky-a" 9 | 10 | window add 3 11 | bspc node -g sticky 12 | 13 | sticky_node_id=$(bspc query -N -n) 14 | 15 | bspc rule -a 'Blah\:\:2:test' -o desktop="test-sticky-b" 16 | 17 | CLASS_NAME=Blah::2 INSTANCE_NAME=test window add 18 | 19 | bspc desktop -f "test-sticky-b" 20 | 21 | bspc query -N -d | grep "$sticky_node_id" > /dev/null || fail "Sticky node is missing in destination." 22 | 23 | window remove 2 24 | bspc desktop -f "test-sticky-a" 25 | window remove 2 26 | 27 | bspc desktop "test-sticky-a" -r 28 | bspc desktop "test-sticky-b" -r 29 | -------------------------------------------------------------------------------- /tests/node/insertion: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-insertion" 6 | bspc desktop -f "test-insertion" 7 | 8 | # Automatic mode 9 | 10 | window add 2 11 | 12 | split_type_a=$(bspc query -T -n @/ | jshon -e splitType -u) 13 | 14 | window add 15 | 16 | split_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u) 17 | 18 | [ "$split_type_a" = "$split_type_b" ] && fail "Non-vacant node insertion should rotate brother." 19 | 20 | split_type_a=$(bspc query -T -n @/ | jshon -e splitType -u) 21 | 22 | bspc rule -a Test:test -o state=floating 23 | window add 24 | 25 | split_type_b=$(bspc query -T -n @/2 | jshon -e splitType -u) 26 | 27 | [ "$split_type_a" = "$split_type_b" ] || fail "Vacant node insertion shouldn't rotate brother." 28 | 29 | window remove 30 | 31 | # Manual mode 32 | 33 | for dir in north west south east ; do 34 | child=1 35 | split_type=vertical 36 | [ "$dir" = "south" -o "$dir" = "east" ] && child=2 37 | [ "$dir" = "north" -o "$dir" = "south" ] && split_type=horizontal 38 | bspc node -p $dir 39 | window add 40 | [ "$(bspc query -N -n)" = "$(bspc query -N -n @parent/${child})" ] || fail "Wrong child polarity for ${dir} preselection." 41 | [ "$(bspc query -T -n @parent | jshon -e splitType -u)" = "$split_type" ] || fail "Wrong split type for ${dir} preselection." 42 | done 43 | 44 | window remove 7 45 | 46 | bspc desktop "test-insertion" -r 47 | -------------------------------------------------------------------------------- /tests/node/receptacle: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-receptacle" 6 | bspc desktop -f "test-receptacle" 7 | 8 | bspc node -i 9 | bspc node @/ -p east -i 10 | bspc node @/2 -p north -i 11 | 12 | bspc rule -a Test:test -o node=@/1 13 | bspc rule -a Test:test -o node=@/2/1 14 | bspc rule -a Test:test -o node=@/2/2 15 | 16 | window add 3 17 | 18 | bspc query -N -n '.leaf.!window.local' > /dev/null && fail "At least one remaining receptacle." 19 | 20 | window remove 3 21 | 22 | bspc desktop "test-receptacle" -r 23 | -------------------------------------------------------------------------------- /tests/node/removal: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-removal" 6 | bspc desktop -f "test-removal" 7 | 8 | window add 3 9 | 10 | next_focus=$(bspc query -N -n); 11 | 12 | bspc node -f @/2/1 13 | bspc node @/2 -k 14 | 15 | [ "$(bspc query -N -n)" = "$next_focus" ] || fail "Invalid focus after removal." 16 | 17 | window remove 18 | 19 | bspc desktop "test-removal" -r 20 | -------------------------------------------------------------------------------- /tests/node/swap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-swap-a" "test-swap-b" 6 | bspc desktop -f "test-swap-a" 7 | 8 | window add 5 9 | next_focus_b=$(bspc query -N -n @/2/2/1) 10 | bspc desktop -f "test-swap-b" 11 | window add 3 12 | 13 | bspc node -f @test-swap-a:/2/2/1 14 | bspc node -a @test-swap-b:/1 15 | 16 | bspc node @/2 -s @test-swap-b:/1 17 | 18 | [ "$(bspc query -N -n @test-swap-b:)" = "$next_focus_b" ] || fail "Invalid focus after swap." 19 | 20 | window remove 2 21 | bspc desktop -f "test-swap-b" 22 | window remove 1 2 23 | window remove 4 24 | 25 | bspc desktop "test-swap-a" -r 26 | bspc desktop "test-swap-b" -r 27 | -------------------------------------------------------------------------------- /tests/node/transfer: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | . ./prelude 4 | 5 | bspc monitor -a "test-transfer-a" "test-transfer-b" 6 | bspc desktop -f "test-transfer-a" 7 | 8 | window add 5 9 | 10 | next_focus_a=$(bspc query -N -n @/1) 11 | next_focus_b=$(bspc query -N -n @/2/2/1) 12 | 13 | bspc node -f $next_focus_b 14 | bspc node @/2 -d "test-transfer-b" 15 | 16 | [ "$next_focus_a" = "$(bspc query -N -n @test-transfer-a:)" ] || fail "Invalid focus after transfer from source." 17 | [ "$next_focus_b" = "$(bspc query -N -n @test-transfer-b:)" ] || fail "Invalid focus after transfer in destination." 18 | 19 | window remove 20 | bspc desktop -f "test-transfer-b" 21 | window remove 1 2 22 | window remove 2 23 | 24 | bspc desktop "test-transfer-a" -r 25 | bspc desktop "test-transfer-b" -r 26 | -------------------------------------------------------------------------------- /tests/prelude: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | fail() { 4 | echo "$@" 1>&2 5 | exit 1 6 | } 7 | 8 | window() { 9 | local action=${1:-add} 10 | local iter=${2:-1} 11 | local delta=${3:-1} 12 | local event=node_${action} 13 | local instance_name=${INSTANCE_NAME:-test} 14 | local class_name=${CLASS_NAME:-Test} 15 | 16 | local cmd 17 | case "$action" in 18 | add) cmd="./test_window $instance_name $class_name" ;; 19 | remove) cmd="bspc node -c" ;; 20 | esac 21 | while [ $iter -gt 0 ] ; do 22 | local rsp_chan=$(bspc subscribe -f -c "$delta" "$event") 23 | $cmd & 24 | cat "$rsp_chan" > /dev/null 25 | iter=$((iter - 1)) 26 | done 27 | } 28 | -------------------------------------------------------------------------------- /tests/run: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | focus_follows_pointer=$(bspc config focus_follows_pointer) 4 | initial_polarity=$(bspc config initial_polarity) 5 | automatic_scheme=$(bspc config automatic_scheme) 6 | bspc config automatic_scheme spiral 7 | bspc config initial_polarity first_child 8 | bspc config focus_follows_pointer false 9 | 10 | cleanup () { 11 | bspc config automatic_scheme "$automatic_scheme" 12 | bspc config initial_polarity "$initial_polarity" 13 | bspc config focus_follows_pointer "$focus_follows_pointer" 14 | } 15 | 16 | abort() { 17 | cleanup 18 | echo "One test failed." 1>&2 19 | exit 1 20 | } 21 | 22 | echo "Node" 23 | echo "-> Insertion" 24 | ./node/insertion || abort 25 | echo "-> Removal" 26 | ./node/removal || abort 27 | echo "-> Transfer" 28 | ./node/transfer || abort 29 | echo "-> Swap" 30 | ./node/swap || abort 31 | echo "-> Flags" 32 | ./node/flags || abort 33 | echo "-> Receptacle" 34 | ./node/receptacle || abort 35 | 36 | echo "Desktop" 37 | echo "-> Transfer" 38 | ./desktop/transfer || abort 39 | echo "-> Swap" 40 | ./desktop/swap || abort 41 | 42 | cleanup 43 | 44 | echo "All tests passed." 45 | -------------------------------------------------------------------------------- /tests/test_window.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TEST_WINDOW_IC "test\0Test" 9 | 10 | bool get_atom(xcb_connection_t *dpy, char *name, xcb_atom_t *atom) 11 | { 12 | bool ret = true; 13 | xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL); 14 | if (reply != NULL) { 15 | *atom = reply->atom; 16 | } else { 17 | ret = false; 18 | } 19 | free(reply); 20 | return ret; 21 | } 22 | 23 | void check_request(xcb_connection_t *dpy, xcb_void_cookie_t cookie, char *msg) 24 | { 25 | xcb_generic_error_t *err = xcb_request_check(dpy, cookie); 26 | if (err != NULL) { 27 | fprintf(stderr, "%s: error code: %u.\n", msg, err->error_code); 28 | xcb_disconnect(dpy); 29 | exit(-1); 30 | } 31 | } 32 | 33 | xcb_gc_t get_font_gc(xcb_connection_t *dpy, xcb_window_t win, const char *font_name) 34 | { 35 | xcb_void_cookie_t ck; 36 | xcb_font_t font = xcb_generate_id(dpy); 37 | ck = xcb_open_font_checked(dpy, font, strlen(font_name), font_name); 38 | check_request(dpy, ck, "Can't open font"); 39 | xcb_gcontext_t gc = xcb_generate_id(dpy); 40 | uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; 41 | uint32_t values[] = {0xffcccccc, 0xff111111, font}; 42 | xcb_create_gc(dpy, gc, win, mask, values); 43 | xcb_close_font(dpy, font); 44 | return gc; 45 | } 46 | 47 | void render_text(xcb_connection_t *dpy, xcb_window_t win, int16_t x, int16_t y) 48 | { 49 | char id[11]; 50 | xcb_void_cookie_t ck; 51 | snprintf(id, sizeof(id), "0x%08X", win); 52 | xcb_gcontext_t gc = get_font_gc(dpy, win, "-*-fixed-medium-*-*-*-18-*-*-*-*-*-*-*"); 53 | /* Doesn't work without _checked */ 54 | ck = xcb_image_text_8_checked(dpy, strlen(id), win, gc, x, y, id); 55 | check_request(dpy, ck, "Can't draw text"); 56 | xcb_free_gc(dpy, gc); 57 | } 58 | 59 | 60 | int main(int argc, char **argv) 61 | { 62 | char *wm_class = TEST_WINDOW_IC; 63 | size_t wm_class_len = sizeof (TEST_WINDOW_IC); 64 | bool will_free_wm_class = false; 65 | 66 | // test instance-name class-name 67 | if (argc > 2) { 68 | will_free_wm_class = true; 69 | size_t len1 = strlen(argv[1]); 70 | size_t len2 = strlen(argv[2]); 71 | // 2 null terminators 72 | wm_class_len = len1 + len2 + 2; 73 | wm_class = malloc(wm_class_len); 74 | 75 | if (!wm_class) return 1; 76 | 77 | memcpy(wm_class, argv[1], len1 + 1); 78 | memcpy(wm_class + len1 + 1, argv[2], len2 + 1); 79 | } 80 | 81 | xcb_connection_t *dpy = xcb_connect(NULL, NULL); 82 | if (dpy == NULL) { 83 | fprintf(stderr, "Can't connect to X.\n"); 84 | return EXIT_FAILURE; 85 | } 86 | xcb_atom_t WM_PROTOCOLS, WM_DELETE_WINDOW; 87 | if (!get_atom(dpy, "WM_PROTOCOLS", &WM_PROTOCOLS) || 88 | !get_atom(dpy, "WM_DELETE_WINDOW", &WM_DELETE_WINDOW)) { 89 | fprintf(stderr, "Can't get required atoms.\n"); 90 | xcb_disconnect(dpy); 91 | return EXIT_FAILURE; 92 | } 93 | xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data; 94 | if (screen == NULL) { 95 | fprintf(stderr, "Can't get current screen.\n"); 96 | xcb_disconnect(dpy); 97 | return EXIT_FAILURE; 98 | } 99 | xcb_window_t win = xcb_generate_id(dpy); 100 | uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; 101 | uint32_t values[] = {0xff111111, XCB_EVENT_MASK_EXPOSURE}; 102 | xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, screen->root, 0, 0, 320, 240, 2, 103 | XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); 104 | xcb_icccm_set_wm_class(dpy, win, wm_class_len, wm_class); 105 | xcb_map_window(dpy, win); 106 | xcb_flush(dpy); 107 | xcb_generic_event_t *evt; 108 | bool running = true; 109 | while (running && (evt = xcb_wait_for_event(dpy)) != NULL) { 110 | uint8_t rt = XCB_EVENT_RESPONSE_TYPE(evt); 111 | if (rt == XCB_CLIENT_MESSAGE) { 112 | xcb_client_message_event_t *cme = (xcb_client_message_event_t *) evt; 113 | if (cme->type == WM_PROTOCOLS && cme->data.data32[0] == WM_DELETE_WINDOW) { 114 | running = false; 115 | } 116 | } else if (rt == XCB_EXPOSE) { 117 | render_text(dpy, win, 12, 24); 118 | } 119 | free(evt); 120 | } 121 | xcb_destroy_window(dpy, win); 122 | xcb_disconnect(dpy); 123 | 124 | if (will_free_wm_class) { 125 | free(wm_class); 126 | } 127 | 128 | return EXIT_SUCCESS; 129 | } 130 | --------------------------------------------------------------------------------