├── .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 | [](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 |
--------------------------------------------------------------------------------