├── .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
├── loop
│ ├── bspwmrc
│ ├── profile
│ ├── sxhkdrc
│ ├── wm
│ └── xinitrc
├── 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
/.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 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
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 | Keyboard and pointer bindings are defined with [sxhkd](https://github.com/baskerville/sxhkd).
23 |
24 | Example configuration files can be found in the [examples](examples) directory.
25 |
26 | ## Monitors, desktops and windows
27 |
28 | *bspwm* holds a list of monitors.
29 |
30 | A monitor is just a rectangle that contains desktops.
31 |
32 | A desktop is just a pointer to a tree.
33 |
34 | Monitors only show the tree of one desktop at a time (their focused desktop).
35 |
36 | The tree is a partition of a monitor's rectangle into smaller rectangular regions.
37 |
38 | Each node in a tree either has zero or two children.
39 |
40 | Each internal node is responsible for splitting a rectangle in half.
41 |
42 | A split is defined by two parameters: the type (horizontal or vertical) and the ratio (a real number *r* such that *0 < r < 1*).
43 |
44 | Each leaf node holds exactly one window.
45 |
46 | ## Insertion modes
47 |
48 | 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.
49 |
50 | The insertion mode tells *bspwm* how it should alter the tree in order to insert new windows on a given insertion point.
51 |
52 | By default the insertion point is the focused window and its insertion mode is *automatic*.
53 |
54 | ### Manual mode
55 |
56 | 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*.
57 |
58 | 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*).
59 |
60 | After doing so the insertion point goes into *manual* mode.
61 |
62 | Let's consider the following scenario:
63 |
64 | ```
65 | a a a
66 | / \ / \ / \
67 | 1 b ---> c b ---> c b
68 | ^ / \ / \ / \ / \ / \
69 | 2 3 4 1 2 3 d 1 2 3
70 | ^ / \
71 | 5 4
72 | ^
73 |
74 | +-----------------------+ +-----------------------+ +-----------------------+
75 | | | | | | | | | | |
76 | | | 2 | | 4 | 2 | | 5 | 4 | 2 |
77 | | | | | ^ | | | ^ | | |
78 | | 1 |-----------| |-----------|-----------| |-----------|-----------|
79 | | ^ | | | | | | | |
80 | | | 3 | | 1 | 3 | | 1 | 3 |
81 | | | | | | | | | |
82 | +-----------------------+ +-----------------------+ +-----------------------+
83 |
84 | X Y Z
85 | ```
86 |
87 | In state *X*, the insertion point is *1*.
88 |
89 | We send the following message to *bspwm*: *node -p north*.
90 |
91 | Then add a new window: *4*, this leads to state *Y*: the new internal node, *c* becomes *a*'s first child.
92 |
93 | Finally we send another message: *node -p west* and add window *5*.
94 |
95 | 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.
96 |
97 | ### Automatic mode
98 |
99 | 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.
100 |
101 | #### Longest side scheme
102 |
103 | 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 choosen based on the dimensions of the tiling rectangle and the initial polarity.
104 |
105 | Let's consider the following scenario, where the initial polarity is set to `second_child`:
106 |
107 | ```
108 | 1 a a
109 | ^ / \ / \
110 | ---> 1 2 ---> 1 b
111 | ^ / \
112 | 2 3
113 | ^
114 |
115 | +-----------------------+ +-----------------------+ +-----------------------+
116 | | | | | | | | |
117 | | | | | | | | 2 |
118 | | | | | | | | |
119 | | 1 | | 1 | 2 | | 1 |-----------|
120 | | ^ | | | ^ | | | |
121 | | | | | | | | 3 |
122 | | | | | | | | ^ |
123 | +-----------------------+ +-----------------------+ +-----------------------+
124 |
125 | X Y Z
126 | ```
127 |
128 | In state *X*, a new window is added.
129 |
130 | Since *1* is wide, it gets split vertically and *2* is added as *a*'s second child given the initial polarity.
131 |
132 | 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.
133 |
134 | #### Spiral scheme
135 |
136 | When the value of the automatic scheme is `spiral`, the window will *take the space* of the insertion point.
137 |
138 | Let's dive into the details with the following scenario:
139 |
140 | ```
141 | a a a
142 | / \ / \ / \
143 | 1 b ---> 1 c ---> 1 d
144 | / \ / \ / \
145 | 2 3 4 b 5 c
146 | ^ ^ / \ ^ / \
147 | 3 2 b 4
148 | / \
149 | 3 2
150 |
151 | +-----------------------+ +-----------------------+ +-----------------------+
152 | | | | | | | | | |
153 | | | 2 | | | 4 | | | 5 |
154 | | | ^ | | | ^ | | | ^ |
155 | | 1 |-----------| | 1 |-----------| | 1 |-----------|
156 | | | | | | | | | | 3 | |
157 | | | 3 | | | 3 | 2 | | |-----| 4 |
158 | | | | | | | | | | 2 | |
159 | +-----------------------+ +-----------------------+ +-----------------------+
160 |
161 | X Y Z
162 | ```
163 |
164 | In state *X*, the insertion point, *2* is in automatic mode.
165 |
166 | 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*.
167 |
168 | The splitting parameters of *b* (type: *horizontal*, ratio: *½*) are copied to *c* and *b* is rotated by 90° clockwise.
169 |
170 | The tiling rectangle of *4* in state *Y* is equal to the tiling rectangle of *2* in state *X*.
171 |
172 | Then the insertion of *5*, with *4* as insertion point, leads to *Z*.
173 |
174 | 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.
175 |
176 |
177 | ## Supported protocols and standards
178 |
179 | - The RandR and Xinerama protocols.
180 | - A subset of the EWMH and ICCCM standards.
181 |
--------------------------------------------------------------------------------
/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.5
--------------------------------------------------------------------------------
/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 split_ratio automatic_scheme initial_polarity directional_focus_tightness borderless_monocle gapless_monocle single_monocle 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 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 split_ratio automatic_scheme initial_polarity directional_focus_tightness borderless_monocle gapless_monocle single_monocle 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 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.3 to 0.9.4
2 |
3 | ## Changes
4 |
5 | - The following events: `node_{manage,unmanage}` are now `node_{add,remove}`.
6 |
7 | ## Additions
8 |
9 | - New monitor/desktop/node descriptors: `any`, `newest`.
10 | - New node flag: `marked`.
11 | - New monitor descriptor: `pointed`.
12 | - New *wm* command: `--reorder-monitors`.
13 | - Receptacles are now described in the manual.
14 | - New `--follow` option added to `node -{m,d,n,s}` and `desktop -{m,s}`.
15 | - The *subscribe* command now has the following options: `--fifo`, `--count`.
16 | - New settings: `ignore_ewmh_fullscreen`, `mapping_events_count`.
17 |
18 | # From 0.9.2 to 0.9.3
19 |
20 | ## Changes
21 |
22 | - *click_to_focus* is now a button name. Specifying a boolean is deprecated but will still work (`true` is equivalent to `button1`).
23 |
24 | ## Additions
25 |
26 | - `node -r` now accepts a relative fraction argument.
27 | - An option was added to `query -{M,D,N}` in order to output names instead of IDs: `--names`.
28 | - New rule consequence: `rectangle=WxH+X+Y`.
29 | - New settings: `swallow_first_click` and `directional_focus_tightness`.
30 |
31 | # From 0.9.1 to 0.9.2
32 |
33 | ## Changes
34 |
35 | - Monitors, desktops and nodes have unique IDs, `bspc query -{N,D,M}` returns IDs and events reference objects by ID instead of name.
36 | - `bspc` fails verbosely and only returns a single non-zero exit code.
37 | - The `DIR` descriptor is based on [right-window](https://github.com/ntrrgc/right-window).
38 | - 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.)
39 | - `bspc query -{N,D,M}` accepts an optional reference argument used by certain descriptors/modifiers.
40 | - Monitors are ordered visually by default.
41 | - The following settings: `border_width`, `window_gap` and `*_padding` behave as expected.
42 | - External rules also receives the monitor, desktop and node selectors computed from the built-in rules stage as subsequent arguments.
43 | - The `focus_follows_pointer` setting is implemented via enter notify events.
44 |
45 | ## Additions
46 |
47 | - Nodes can be hidden/shown via the new `hidden` flag.
48 | - Node receptacles can be inserted with `node -i`. An example is given in `git show e8aa679`.
49 | - Non-tiled nodes can be moved/resized via `node -{v,z}`.
50 | - 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`.
51 | - Node descriptors: ``, `pointed`.
52 | - Node modifiers: `hidden`, `descendant_of`, `ancestor_of`, `window`, `active`. Example: `bspc query -N 0x00400006 -n .descendant_of` returns the descendants of `0x00400006`.
53 | - Desktop descriptor: ``.
54 | - Monitor descriptor: ``.
55 | - Settings: `pointer_motion_interval`, `pointer_modifier`, `pointer_action{1,2,3}`, `click_to_focus`, `honor_size_hints`.
56 | - Event: `pointer_action`.
57 | - ICCCM/EWMH atoms: `WM_STATE`, `_NET_WM_STRUT_PARTIAL`.
58 | - `bspc` shell completions for `fish`.
59 |
60 | ## Removals
61 |
62 | - The `pointer` domain. Pointer actions are handled internally. You need to remove any binding that uses this domain from your `sxhkdrc`.
63 | - Settings: `history_aware_focus`, `focus_by_distance`. Both settings are merged into the new `DIR` implementation.
64 | - `monitor -r|--remove-desktops`: use `desktop -r|--remove` instead.
65 | - `wm -r|--remove-monitor`: use `monitor -r|--remove` instead.
66 |
67 | # From 0.9 to 0.9.1
68 |
69 | ## Overview
70 |
71 | 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.
72 |
73 | ## Changes
74 |
75 | - All the commands that started with `window` now start with `node`.
76 | - `-W|--windows`, `-w|--window`, `-w|--to-window` are now `-N|--nodes`, `-n|--node`, `-n|--to-node`.
77 | - 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).
78 | - The `WINDOW_SEL` becomes `NODE_SEL` and now contains a `PATH` specifier to select internal nodes.
79 | - The `control` domain is renamed to `wm`.
80 | - `restore -{T,H,S}` was unified into `wm -l|--load-state` and `query -{T,H,S}` into `wm -d|--dump-state`.
81 | - `control --subscribe` becomes `subscribe`.
82 | - `node --toggle` (previously `window --toggle`) is split into `node --state` and `node --flag`.
83 | - The preselection direction (resp. ratio) is now set with `node --presel-dir|-p` (resp. `node --presel-ratio|-o`).
84 | - The following desktop commands: `--rotate`, `--flip`, `--balance`, `--equalize`, `--circulate` are now node commands.
85 | - `query -T ...` outputs JSON.
86 | - `query -{M,D,N}`: the descriptor part of the selector is now optional (e.g.: `query -D -d .urgent`).
87 | - 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.).
88 | - Modifiers can now be applied to any descriptor (e.g.: `query -N -n 0x80000d.floating`).
89 | - `wm -l` (previously `restore -T`) will now destroy the existing tree and restore from scratch instead of relying on existing monitors and desktops.
90 | - `subscribe` (previously `control --subscribe`) now accepts arguments and can receive numerous events from different domains (see the *EVENTS* section of the manual).
91 | - `rule -a`: it is now possible to specify the class name *and* instance name (e.g.: `rule -a Foo:bar`).
92 | - `presel_border_color` is now `presel_feedback_color`.
93 | - `bspwm -v` yields an accurate version.
94 | - The monitors are sorted, by default, according to the natural visual hierarchy.
95 |
96 | ## Additions
97 |
98 | ### Settings
99 |
100 | - `single_monocle`.
101 | - `paddingless_monocle`.
102 |
103 | ### Commands
104 |
105 | - `{node,desktop} --activate`.
106 | - `node --layer`.
107 | - `desktop --bubble`.
108 | - `wm {--add-monitor,--remove-monitor}`.
109 | - `monitor --rectangle`.
110 |
111 | ## Removals
112 |
113 | ### Commands
114 |
115 | - `desktop --toggle`
116 | - `desktop --cancel-presel`
117 | - `control --toggle-visibility`.
118 |
119 | ### Settings
120 |
121 | - `apply_floating_atom`.
122 | - `auto_alternate`.
123 | - `auto_cancel`.
124 | - `focused_locked_border_color`
125 | - `active_locked_border_color`
126 | - `normal_locked_border_color`
127 | - `focused_sticky_border_color`
128 | - `active_sticky_border_color`
129 | - `normal_sticky_border_color`
130 | - `focused_private_border_color`
131 | - `active_private_border_color`
132 | - `normal_private_border_color`
133 | - `urgent_border_color`
134 |
135 | ## Message Translation Guide
136 |
137 | 0.9 | 0.9.1
138 | -----------------------------------------|----------------------------------
139 | `{left,down,up,right}` | `{west,south,north,east}`
140 | `window -r` | `node -o` (`node -r` also exists)
141 | `window -e DIR RATIO` | `node @DIR -r RATIO`
142 | `window -R DIR DEG` | `node @DIR -R DEG`
143 | `window -w` | `node -n`
144 | `desktop DESKTOP_SEL -R DEG` | `node @DESKTOP_SEL:/ -R DEG`
145 | `desktop DESKTOP_SEL -E` | `node @DESKTOP_SEL:/ -E`
146 | `desktop DESKTOP_SEL -B` | `node @DESKTOP_SEL:/ -B`
147 | `desktop DESKTOP_SEL -C forward|backward`| `node @DESKTOP_SEL:/ -C forward|backward`
148 | `desktop DESKTOP_SEL --cancel-presel` | `bspc query -N -d DESKTOP_SEL | xargs -I id -n 1 bspc node id -p cancel`
149 | `window -t floating` | `node -t ~floating`
150 | `query -W -w` | `query -N -n .leaf`
151 | `query -{T,H,S}` | `wm -d`
152 | `restore -{T,H,S}` | `wm -l`
153 |
--------------------------------------------------------------------------------
/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 | [1]: https://www.bell-labs.com/usr/dmr/www/cbook/
21 | [2]: https://xcb.freedesktop.org/tutorial/
22 | [3]: http://git-scm.com/documentation
23 | [4]: https://www.kernel.org/doc/Documentation/process/coding-style.rst
24 | [5]: http://lea.verou.me/2012/01/why-tabs-are-clearly-superior/
25 |
26 | ## Donations
27 |
28 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RHTYMMB9SHP68)
29 |
--------------------------------------------------------------------------------
/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 | - [packages](http://download.opensuse.org/repositories/home:/Head_on_a_Stick:/bspwm/Debian_8.0/)
25 | - [guide](http://forums.debian.net/viewtopic.php?f=16&t=127708)
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 | 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 |
7 | if [ "$instance" = fontforge ] ; then
8 | title=$(xtitle "$wid")
9 | case "$title" in
10 | Layers|Tools|Warning)
11 | echo "focus = off"
12 | ;;
13 | esac
14 | fi
15 |
--------------------------------------------------------------------------------
/examples/loop/bspwmrc:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | if [ -e "$BSPWM_STATE" ] ; then
4 | bspc wm -l "$BSPWM_STATE"
5 | rm "$BSPWM_STATE"
6 | fi
7 |
--------------------------------------------------------------------------------
/examples/loop/profile:
--------------------------------------------------------------------------------
1 | export BSPWM_STATE=/tmp/bspwm-state.json
2 |
--------------------------------------------------------------------------------
/examples/loop/sxhkdrc:
--------------------------------------------------------------------------------
1 | # refresh or quit bspwm
2 | super + alt + {_,shift + }Escape
3 | {bspc wm -d > "$BSPWM_STATE" && bspc quit, \
4 | bspc quit 1}
5 |
--------------------------------------------------------------------------------
/examples/loop/wm:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | while true ; do
4 | bspwm || break
5 | done
6 |
--------------------------------------------------------------------------------
/examples/loop/xinitrc:
--------------------------------------------------------------------------------
1 | exec wm
2 |
--------------------------------------------------------------------------------
/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 | [ -e "$PANEL_FIFO" ] && bspc subscribe report > "$PANEL_FIFO" &
4 | pgrep -x panel > /dev/null || panel &
5 |
--------------------------------------------------------------------------------
/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 takes 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 bspwm normally
22 | super + alt + Escape
23 | bspc quit
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 node
38 | super + g
39 | bspc node -s biggest
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 node in the current desktop
66 | super + {_,shift + }c
67 | bspc node -f {next,prev}.local
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 "helpers.h"
34 | #include "common.h"
35 |
36 | int main(int argc, char *argv[])
37 | {
38 | int sock_fd;
39 | struct sockaddr_un sock_address;
40 | char msg[BUFSIZ], rsp[BUFSIZ];
41 |
42 | if (argc < 2) {
43 | err("No arguments given.\n");
44 | }
45 |
46 | sock_address.sun_family = AF_UNIX;
47 | char *sp;
48 |
49 | if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
50 | err("Failed to create the socket.\n");
51 | }
52 |
53 | sp = getenv(SOCKET_ENV_VAR);
54 | if (sp != NULL) {
55 | snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
56 | } else {
57 | char *host = NULL;
58 | int dn = 0, sn = 0;
59 | if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
60 | snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
61 | }
62 | free(host);
63 | }
64 |
65 | if (connect(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
66 | err("Failed to connect to the socket.\n");
67 | }
68 |
69 | argc--, argv++;
70 | int msg_len = 0;
71 |
72 | for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {
73 | n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
74 | msg_len += n;
75 | }
76 |
77 | if (send(sock_fd, msg, msg_len, 0) == -1) {
78 | err("Failed to send the data.\n");
79 | }
80 |
81 | int ret = EXIT_SUCCESS, nb;
82 |
83 | struct pollfd fds[] = {
84 | {sock_fd, POLLIN, 0},
85 | {STDOUT_FILENO, POLLHUP, 0},
86 | };
87 |
88 | while (poll(fds, 2, -1) > 0) {
89 | if (fds[1].revents & (POLLERR | POLLHUP)) {
90 | break;
91 | }
92 | if (fds[0].revents & POLLIN) {
93 | if ((nb = recv(sock_fd, rsp, sizeof(rsp)-1, 0)) > 0) {
94 | rsp[nb] = '\0';
95 | if (rsp[0] == FAILURE_MESSAGE[0]) {
96 | ret = EXIT_FAILURE;
97 | printf("%s", rsp + 1);
98 | } else {
99 | printf("%s", rsp);
100 | }
101 | fflush(stdout);
102 | } else {
103 | break;
104 | }
105 | }
106 | }
107 |
108 | close(sock_fd);
109 | return ret;
110 | }
111 |
--------------------------------------------------------------------------------
/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 ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS)
36 | #define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
37 | #define BSPWM_CLASS_NAME "Bspwm"
38 | #define META_WINDOW_IC "wm\0" BSPWM_CLASS_NAME
39 | #define ROOT_WINDOW_IC "root\0" BSPWM_CLASS_NAME
40 | #define PRESEL_FEEDBACK_I "presel_feedback"
41 | #define PRESEL_FEEDBACK_IC PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME
42 | #define MOTION_RECORDER_I "motion_recorder"
43 | #define MOTION_RECORDER_IC MOTION_RECORDER_I "\0" BSPWM_CLASS_NAME
44 |
45 | xcb_connection_t *dpy;
46 | int default_screen, screen_width, screen_height;
47 | uint32_t clients_count;
48 | xcb_screen_t *screen;
49 | xcb_window_t root;
50 | char config_path[MAXLEN];
51 |
52 | monitor_t *mon;
53 | monitor_t *mon_head;
54 | monitor_t *mon_tail;
55 | monitor_t *pri_mon;
56 | history_t *history_head;
57 | history_t *history_tail;
58 | history_t *history_needle;
59 | rule_t *rule_head;
60 | rule_t *rule_tail;
61 | stacking_list_t *stack_head;
62 | stacking_list_t *stack_tail;
63 | subscriber_list_t *subscribe_head;
64 | subscriber_list_t *subscribe_tail;
65 | pending_rule_t *pending_rule_head;
66 | pending_rule_t *pending_rule_tail;
67 |
68 | xcb_window_t meta_window;
69 | struct {
70 | xcb_window_t id;
71 | uint16_t sequence;
72 | bool enabled;
73 | } motion_recorder;
74 | xcb_atom_t WM_STATE;
75 | xcb_atom_t WM_TAKE_FOCUS;
76 | xcb_atom_t WM_DELETE_WINDOW;
77 | int exit_status;
78 |
79 | bool auto_raise;
80 | bool sticky_still;
81 | bool record_history;
82 | bool running;
83 | bool randr;
84 |
85 | void init(void);
86 | void setup(void);
87 | void register_events(void);
88 | void cleanup(void);
89 | bool check_connection (xcb_connection_t *dpy);
90 | void sig_handler(int sig);
91 |
92 | #endif
93 |
--------------------------------------------------------------------------------
/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 | void focus_desktop(monitor_t *m, desktop_t *d);
31 | bool activate_desktop(monitor_t *m, desktop_t *d);
32 | bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t *sel);
33 | bool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel);
34 | bool set_layout(monitor_t *m, desktop_t *d, layout_t l);
35 | void handle_presel_feedbacks(monitor_t *m, desktop_t *d);
36 | bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d, bool follow);
37 | desktop_t *make_desktop(const char *name, uint32_t id);
38 | void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
39 | void insert_desktop(monitor_t *m, desktop_t *d);
40 | void add_desktop(monitor_t *m, desktop_t *d);
41 | desktop_t *find_desktop_in(uint32_t id, monitor_t *m);
42 | void unlink_desktop(monitor_t *m, desktop_t *d);
43 | void remove_desktop(monitor_t *m, desktop_t *d);
44 | void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
45 | bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2, bool follow);
46 | void show_desktop(desktop_t *d);
47 | void hide_desktop(desktop_t *d);
48 | bool is_urgent(desktop_t *d);
49 |
50 | #endif
51 |
--------------------------------------------------------------------------------
/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 | 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 | void ewmh_init(void)
35 | {
36 | ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
37 | if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
38 | err("Can't initialize EWMH atoms.\n");
39 | }
40 | }
41 |
42 | void ewmh_update_active_window(void)
43 | {
44 | xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
45 | xcb_ewmh_set_active_window(ewmh, default_screen, win);
46 | }
47 |
48 | void ewmh_update_number_of_desktops(void)
49 | {
50 | uint32_t desktops_count = 0;
51 |
52 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
53 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
54 | desktops_count++;
55 | }
56 | }
57 |
58 | xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
59 | }
60 |
61 | uint32_t ewmh_get_desktop_index(desktop_t *d)
62 | {
63 | uint32_t i = 0;
64 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
65 | for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
66 | if (d == cd) {
67 | return i;
68 | }
69 | }
70 | }
71 | return 0;
72 | }
73 |
74 | bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
75 | {
76 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
77 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
78 | if (i == 0) {
79 | loc->monitor = m;
80 | loc->desktop = d;
81 | loc->node = NULL;
82 | return true;
83 | }
84 | }
85 | }
86 | return false;
87 | }
88 |
89 | void ewmh_update_current_desktop(void)
90 | {
91 | if (mon == NULL) {
92 | return;
93 | }
94 | uint32_t i = ewmh_get_desktop_index(mon->desk);
95 | xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
96 | }
97 |
98 | void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
99 | {
100 | uint32_t i = ewmh_get_desktop_index(d);
101 | for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
102 | if (f->client == NULL) {
103 | continue;
104 | }
105 | xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
106 | }
107 | }
108 |
109 | void ewmh_update_wm_desktops(void)
110 | {
111 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
112 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
113 | uint32_t i = ewmh_get_desktop_index(d);
114 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
115 | if (n->client == NULL) {
116 | continue;
117 | }
118 | xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
119 | }
120 | }
121 | }
122 | }
123 |
124 | void ewmh_update_desktop_names(void)
125 | {
126 | char names[MAXLEN];
127 | unsigned int i, j;
128 | uint32_t names_len;
129 | i = 0;
130 |
131 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
132 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
133 | for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
134 | names[i + j] = d->name[j];
135 | }
136 | i += j;
137 | if (i < sizeof(names)) {
138 | names[i++] = '\0';
139 | }
140 | }
141 | }
142 |
143 | if (i < 1) {
144 | xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
145 | return;
146 | }
147 |
148 | names_len = i - 1;
149 | xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
150 | }
151 |
152 | void ewmh_update_desktop_viewport(void)
153 | {
154 | uint32_t desktops_count = 0;
155 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
156 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
157 | desktops_count++;
158 | }
159 | }
160 | if (desktops_count == 0) {
161 | xcb_ewmh_set_desktop_viewport(ewmh, default_screen, 0, NULL);
162 | return;
163 | }
164 | xcb_ewmh_coordinates_t coords[desktops_count];
165 | uint16_t desktop = 0;
166 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
167 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
168 | coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};
169 | }
170 | }
171 | xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);
172 | }
173 |
174 | bool ewmh_handle_struts(xcb_window_t win)
175 | {
176 | xcb_ewmh_wm_strut_partial_t struts;
177 | bool changed = false;
178 | if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {
179 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
180 | xcb_rectangle_t rect = m->rectangle;
181 | if (rect.x < (int16_t) struts.left &&
182 | (int16_t) struts.left < (rect.x + rect.width - 1) &&
183 | (int16_t) struts.left_end_y >= rect.y &&
184 | (int16_t) struts.left_start_y < (rect.y + rect.height)) {
185 | int dx = struts.left - rect.x;
186 | if (m->padding.left < 0) {
187 | m->padding.left += dx;
188 | } else {
189 | m->padding.left = MAX(dx, m->padding.left);
190 | }
191 | changed = true;
192 | }
193 | if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&
194 | (int16_t) (screen_width - struts.right) > rect.x &&
195 | (int16_t) struts.right_end_y >= rect.y &&
196 | (int16_t) struts.right_start_y < (rect.y + rect.height)) {
197 | int dx = (rect.x + rect.width) - screen_width + struts.right;
198 | if (m->padding.right < 0) {
199 | m->padding.right += dx;
200 | } else {
201 | m->padding.right = MAX(dx, m->padding.right);
202 | }
203 | changed = true;
204 | }
205 | if (rect.y < (int16_t) struts.top &&
206 | (int16_t) struts.top < (rect.y + rect.height - 1) &&
207 | (int16_t) struts.top_end_x >= rect.x &&
208 | (int16_t) struts.top_start_x < (rect.x + rect.width)) {
209 | int dy = struts.top - rect.y;
210 | if (m->padding.top < 0) {
211 | m->padding.top += dy;
212 | } else {
213 | m->padding.top = MAX(dy, m->padding.top);
214 | }
215 | changed = true;
216 | }
217 | if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&
218 | (int16_t) (screen_height - struts.bottom) > rect.y &&
219 | (int16_t) struts.bottom_end_x >= rect.x &&
220 | (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {
221 | int dy = (rect.y + rect.height) - screen_height + struts.bottom;
222 | if (m->padding.bottom < 0) {
223 | m->padding.bottom += dy;
224 | } else {
225 | m->padding.bottom = MAX(dy, m->padding.bottom);
226 | }
227 | changed = true;
228 | }
229 | }
230 | }
231 | return changed;
232 | }
233 |
234 | void ewmh_update_client_list(bool stacking)
235 | {
236 | if (clients_count == 0) {
237 | xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
238 | xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
239 | return;
240 | }
241 |
242 | xcb_window_t wins[clients_count];
243 | unsigned int i = 0;
244 |
245 | if (stacking) {
246 | for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
247 | wins[i++] = s->node->id;
248 | }
249 | xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
250 | } else {
251 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
252 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
253 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
254 | if (n->client == NULL) {
255 | continue;
256 | }
257 | wins[i++] = n->id;
258 | }
259 | }
260 | }
261 | xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
262 | }
263 | }
264 |
265 | void ewmh_wm_state_update(node_t *n)
266 | {
267 | client_t *c = n->client;
268 | size_t count = 0;
269 | uint32_t values[12];
270 | #define HANDLE_WM_STATE(s) \
271 | if (WM_FLAG_##s & c->wm_flags) { \
272 | values[count++] = ewmh->_NET_WM_STATE_##s; \
273 | }
274 | HANDLE_WM_STATE(MODAL)
275 | HANDLE_WM_STATE(STICKY)
276 | HANDLE_WM_STATE(MAXIMIZED_VERT)
277 | HANDLE_WM_STATE(MAXIMIZED_HORZ)
278 | HANDLE_WM_STATE(SHADED)
279 | HANDLE_WM_STATE(SKIP_TASKBAR)
280 | HANDLE_WM_STATE(SKIP_PAGER)
281 | HANDLE_WM_STATE(HIDDEN)
282 | HANDLE_WM_STATE(FULLSCREEN)
283 | HANDLE_WM_STATE(ABOVE)
284 | HANDLE_WM_STATE(BELOW)
285 | HANDLE_WM_STATE(DEMANDS_ATTENTION)
286 | #undef HANDLE_WM_STATE
287 | xcb_ewmh_set_wm_state(ewmh, n->id, count, values);
288 | }
289 |
290 | void ewmh_set_supporting(xcb_window_t win)
291 | {
292 | pid_t wm_pid = getpid();
293 | xcb_ewmh_set_supporting_wm_check(ewmh, root, win);
294 | xcb_ewmh_set_supporting_wm_check(ewmh, win, win);
295 | xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);
296 | xcb_ewmh_set_wm_pid(ewmh, win, wm_pid);
297 | }
298 |
--------------------------------------------------------------------------------
/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 | 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 | unsigned int area(xcb_rectangle_t r)
37 | {
38 | return r.width * r.height;
39 | }
40 |
41 | /* Distance between the `dir` edge of `r1` and the `opposite(dir)` edge of `r2`. */
42 | uint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
43 | {
44 | xcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1};
45 | xcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1};
46 | switch (dir) {
47 | case DIR_NORTH:
48 | return r2_max.y > r1.y ? r2_max.y - r1.y : r1.y - r2_max.y;
49 | break;
50 | case DIR_WEST:
51 | return r2_max.x > r1.x ? r2_max.x - r1.x : r1.x - r2_max.x;
52 | break;
53 | case DIR_SOUTH:
54 | return r2.y < r1_max.y ? r1_max.y - r2.y : r2.y - r1_max.y;
55 | break;
56 | case DIR_EAST:
57 | return r2.x < r1_max.x ? r1_max.x - r2.x : r2.x - r1_max.x;
58 | break;
59 | default:
60 | return UINT32_MAX;
61 | }
62 | }
63 |
64 | /* Is `r2` on the `dir` side of `r1`? */
65 | bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
66 | {
67 | xcb_point_t r1_max = {r1.x + r1.width - 1, r1.y + r1.height - 1};
68 | xcb_point_t r2_max = {r2.x + r2.width - 1, r2.y + r2.height - 1};
69 |
70 | /* Eliminate rectangles on the opposite side */
71 | switch (directional_focus_tightness) {
72 | case TIGHTNESS_LOW:
73 | switch (dir) {
74 | case DIR_NORTH:
75 | if (r2.y > r1_max.y) {
76 | return false;
77 | }
78 | break;
79 | case DIR_WEST:
80 | if (r2.x > r1_max.x) {
81 | return false;
82 | }
83 | break;
84 | case DIR_SOUTH:
85 | if (r2_max.y < r1.y) {
86 | return false;
87 | }
88 | break;
89 | case DIR_EAST:
90 | if (r2_max.x < r1.x) {
91 | return false;
92 | }
93 | break;
94 | default:
95 | return false;
96 | }
97 | break;
98 | case TIGHTNESS_HIGH:
99 | switch (dir) {
100 | case DIR_NORTH:
101 | if (r2.y >= r1.y) {
102 | return false;
103 | }
104 | break;
105 | case DIR_WEST:
106 | if (r2.x >= r1.x) {
107 | return false;
108 | }
109 | break;
110 | case DIR_SOUTH:
111 | if (r2_max.y <= r1_max.y) {
112 | return false;
113 | }
114 | break;
115 | case DIR_EAST:
116 | if (r2_max.x <= r1_max.x) {
117 | return false;
118 | }
119 | break;
120 | default:
121 | return false;
122 | }
123 | break;
124 | default:
125 | return false;
126 | }
127 |
128 | /* Is there a shared vertical/horizontal range? */
129 | switch (dir) {
130 | case DIR_NORTH:
131 | case DIR_SOUTH:
132 | return
133 | (r2.x >= r1.x && r2.x <= r1_max.x) ||
134 | (r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||
135 | (r1.x > r2.x && r1.x < r2_max.x);
136 | break;
137 | case DIR_WEST:
138 | case DIR_EAST:
139 | return
140 | (r2.y >= r1.y && r2.y <= r1_max.y) ||
141 | (r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||
142 | (r1.y > r2.y && r1_max.y < r2_max.y);
143 | break;
144 | default:
145 | return false;
146 | }
147 | }
148 |
149 | bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)
150 | {
151 | return (a.x == b.x && a.y == b.y &&
152 | a.width == b.width && a.height == b.height);
153 | }
154 |
155 | int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)
156 | {
157 | if (r1.y >= (r2.y + r2.height)) {
158 | return 1;
159 | } else if (r2.y >= (r1.y + r1.height)) {
160 | return -1;
161 | } else {
162 | if (r1.x >= (r2.x + r2.width)) {
163 | return 1;
164 | } else if (r2.x >= (r1.x + r1.width)) {
165 | return -1;
166 | } else {
167 | return area(r2) - area(r1);
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/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 | unsigned int area(xcb_rectangle_t r);
33 | uint32_t boundary_distance(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
34 | bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
35 | bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);
36 | int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/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 | int size = 0;
156 | va_list args;
157 | va_start(args, fmt);
158 | size = vasprintf(buf, fmt, args);
159 | va_end(args);
160 | return size;
161 | }
162 |
163 | int vasprintf(char **buf, const char *fmt, va_list args)
164 | {
165 | va_list tmp;
166 | va_copy(tmp, args);
167 | int size = vsnprintf(NULL, 0, fmt, tmp);
168 | va_end(tmp);
169 |
170 | if (size < 0) {
171 | return -1;
172 | }
173 |
174 | *buf = malloc(size + 1);
175 |
176 | if (*buf == NULL) {
177 | return -1;
178 | }
179 |
180 | size = vsprintf(*buf, fmt, args);
181 | return size;
182 | }
183 |
184 | /* Adapted from i3wm */
185 | uint32_t get_color_pixel(const char *color)
186 | {
187 | unsigned int red, green, blue;
188 | if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
189 | /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
190 | * color depth visual. */
191 | return (0xFF << 24) | (red << 16 | green << 8 | blue);
192 | } else {
193 | return screen->black_pixel;
194 | }
195 | }
196 |
197 | bool is_hex_color(const char *color)
198 | {
199 | if (color[0] != '#' || strlen(color) != 7) {
200 | return false;
201 | }
202 | for (int i = 1; i < 7; i++) {
203 | if (!isxdigit(color[i])) {
204 | return false;
205 | }
206 | }
207 | return true;
208 | }
209 |
--------------------------------------------------------------------------------
/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 | #define IS_MONOCLE(d) (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root, true) <= 1))
44 |
45 | #define BOOL_STR(A) ((A) ? "true" : "false")
46 | #define ON_OFF_STR(A) ((A) ? "on" : "off")
47 | #define LAYOUT_STR(A) ((A) == LAYOUT_TILED ? "tiled" : "monocle")
48 | #define LAYOUT_CHR(A) ((A) == LAYOUT_TILED ? 'T' : 'M')
49 | #define CHILD_POL_STR(A) ((A) == FIRST_CHILD ? "first_child" : "second_child")
50 | #define AUTO_SCM_STR(A) ((A) == SCHEME_LONGEST_SIDE ? "longest_side" : "spiral")
51 | #define TIGHTNESS_STR(A) ((A) == TIGHTNESS_HIGH ? "high" : "low")
52 | #define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
53 | #define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
54 | #define SPLIT_DIR_STR(A) ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
55 | #define STATE_STR(A) ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
56 | #define STATE_CHR(A) ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
57 | #define LAYER_STR(A) ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
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 MAXLEN 256
64 | #define SMALEN 32
65 | #define INIT_CAP 8
66 |
67 | #define cleaned_mask(m) (m & ~(num_lock | scroll_lock | caps_lock))
68 | #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
69 | #define unsigned_subtract(a, b) \
70 | do { \
71 | if (b > a) { \
72 | a = 0; \
73 | } else { \
74 | a -= b; \
75 | } \
76 | } while (false)
77 |
78 |
79 | void warn(char *fmt, ...);
80 | void err(char *fmt, ...);
81 | char *read_string(const char *file_path, size_t *tlen);
82 | char *copy_string(char *str, size_t len);
83 | char *mktempfifo(const char *template);
84 | int asprintf(char **buf, const char *fmt, ...);
85 | int vasprintf(char **buf, const char *fmt, va_list args);
86 | uint32_t get_color_pixel(const char *color);
87 | bool is_hex_color(const char *color);
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/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 pull.
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 | * Filsl 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 | break;
202 | }
203 | token = &tokens[token->parent];
204 | }
205 | #else
206 | for (i = parser->toknext - 1; i >= 0; i--) {
207 | token = &tokens[i];
208 | if (token->start != -1 && token->end == -1) {
209 | if (token->type != type) {
210 | return JSMN_ERROR_INVAL;
211 | }
212 | parser->toksuper = -1;
213 | token->end = parser->pos + 1;
214 | break;
215 | }
216 | }
217 | /* Error if unmatched closing bracket */
218 | if (i == -1) return JSMN_ERROR_INVAL;
219 | for (; i >= 0; i--) {
220 | token = &tokens[i];
221 | if (token->start != -1 && token->end == -1) {
222 | parser->toksuper = i;
223 | break;
224 | }
225 | }
226 | #endif
227 | break;
228 | case '\"':
229 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
230 | if (r < 0) return r;
231 | count++;
232 | if (parser->toksuper != -1 && tokens != NULL)
233 | tokens[parser->toksuper].size++;
234 | break;
235 | case '\t' : case '\r' : case '\n' : case ' ':
236 | break;
237 | case ':':
238 | parser->toksuper = parser->toknext - 1;
239 | break;
240 | case ',':
241 | if (tokens != NULL && parser->toksuper != -1 &&
242 | tokens[parser->toksuper].type != JSMN_ARRAY &&
243 | tokens[parser->toksuper].type != JSMN_OBJECT) {
244 | #ifdef JSMN_PARENT_LINKS
245 | parser->toksuper = tokens[parser->toksuper].parent;
246 | #else
247 | for (i = parser->toknext - 1; i >= 0; i--) {
248 | if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
249 | if (tokens[i].start != -1 && tokens[i].end == -1) {
250 | parser->toksuper = i;
251 | break;
252 | }
253 | }
254 | }
255 | #endif
256 | }
257 | break;
258 | #ifdef JSMN_STRICT
259 | /* In strict mode primitives are: numbers and booleans */
260 | case '-': case '0': case '1' : case '2': case '3' : case '4':
261 | case '5': case '6': case '7' : case '8': case '9':
262 | case 't': case 'f': case 'n' :
263 | /* And they must not be keys of the object */
264 | if (tokens != NULL && parser->toksuper != -1) {
265 | jsmntok_t *t = &tokens[parser->toksuper];
266 | if (t->type == JSMN_OBJECT ||
267 | (t->type == JSMN_STRING && t->size != 0)) {
268 | return JSMN_ERROR_INVAL;
269 | }
270 | }
271 | #else
272 | /* In non-strict mode every unquoted value is a primitive */
273 | default:
274 | #endif
275 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
276 | if (r < 0) return r;
277 | count++;
278 | if (parser->toksuper != -1 && tokens != NULL)
279 | tokens[parser->toksuper].size++;
280 | break;
281 |
282 | #ifdef JSMN_STRICT
283 | /* Unexpected char in strict mode */
284 | default:
285 | return JSMN_ERROR_INVAL;
286 | #endif
287 | }
288 | }
289 |
290 | if (tokens != NULL) {
291 | for (i = parser->toknext - 1; i >= 0; i--) {
292 | /* Unmatched opened object or array */
293 | if (tokens[i].start != -1 && tokens[i].end == -1) {
294 | return JSMN_ERROR_PART;
295 | }
296 | }
297 | }
298 |
299 | return count;
300 | }
301 |
302 | /**
303 | * Creates a new parser based over a given buffer with an array of tokens
304 | * available.
305 | */
306 | void jsmn_init(jsmn_parser *parser) {
307 | parser->pos = 0;
308 | parser->toknext = 0;
309 | parser->toksuper = -1;
310 | }
311 |
312 |
--------------------------------------------------------------------------------
/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 | * @param type type (object, array, string etc.)
37 | * @param start start position in JSON data string
38 | * @param 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 focus_monitor(monitor_t *m);
39 | void add_monitor(monitor_t *m);
40 | void unlink_monitor(monitor_t *m);
41 | void remove_monitor(monitor_t *m);
42 | void merge_monitors(monitor_t *ms, monitor_t *md);
43 | bool swap_monitors(monitor_t *m1, monitor_t *m2);
44 | monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t *sel);
45 | bool is_inside_monitor(monitor_t *m, xcb_point_t pt);
46 | monitor_t *monitor_from_point(xcb_point_t pt);
47 | monitor_t *monitor_from_client(client_t *c);
48 | monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t *sel);
49 | bool find_any_monitor(coordinates_t *ref, coordinates_t *dst, monitor_select_t *sel);
50 | bool update_monitors(void);
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/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_state_transition(char *s, state_transition_t *m);
30 | bool parse_tightness(char *s, tightness_t *t);
31 | bool parse_degree(char *s, int *d);
32 | bool parse_id(char *s, uint32_t *id);
33 | bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
34 | bool parse_index(char *s, uint16_t *idx);
35 | bool parse_rectangle(char *s, xcb_rectangle_t *r);
36 | bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
37 | bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
38 | bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
39 | bool parse_node_modifiers(char *desc, node_select_t *sel);
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/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 | void pointer_init(void)
40 | {
41 | num_lock = modfield_from_keysym(XK_Num_Lock);
42 | caps_lock = modfield_from_keysym(XK_Caps_Lock);
43 | scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
44 | if (caps_lock == XCB_NO_SYMBOL) {
45 | caps_lock = XCB_MOD_MASK_LOCK;
46 | }
47 | grabbing = false;
48 | grabbed_node = NULL;
49 | }
50 |
51 | void window_grab_buttons(xcb_window_t win)
52 | {
53 | for (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {
54 | if (click_to_focus == (int8_t) XCB_BUTTON_INDEX_ANY || click_to_focus == (int8_t) BUTTONS[i]) {
55 | window_grab_button(win, BUTTONS[i], XCB_NONE);
56 | }
57 | if (pointer_actions[i] != ACTION_NONE) {
58 | window_grab_button(win, BUTTONS[i], pointer_modifier);
59 | }
60 | }
61 | }
62 |
63 | void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
64 | {
65 | #define GRAB(b, m) \
66 | xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
67 | XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
68 | GRAB(button, modifier);
69 | if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
70 | GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
71 | }
72 | if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
73 | GRAB(button, modifier | num_lock | caps_lock);
74 | }
75 | if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
76 | GRAB(button, modifier | caps_lock | scroll_lock);
77 | }
78 | if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
79 | GRAB(button, modifier | num_lock | scroll_lock);
80 | }
81 | if (num_lock != XCB_NO_SYMBOL) {
82 | GRAB(button, modifier | num_lock);
83 | }
84 | if (caps_lock != XCB_NO_SYMBOL) {
85 | GRAB(button, modifier | caps_lock);
86 | }
87 | if (scroll_lock != XCB_NO_SYMBOL) {
88 | GRAB(button, modifier | scroll_lock);
89 | }
90 | #undef GRAB
91 | }
92 |
93 | void grab_buttons(void)
94 | {
95 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
96 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
97 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
98 | window_grab_buttons(n->id);
99 | if (n->presel != NULL) {
100 | window_grab_buttons(n->presel->feedback);
101 | }
102 | }
103 | }
104 | }
105 | }
106 |
107 | void ungrab_buttons(void)
108 | {
109 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
110 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
111 | for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
112 | xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
113 | }
114 | }
115 | }
116 | }
117 |
118 | int16_t modfield_from_keysym(xcb_keysym_t keysym)
119 | {
120 | uint16_t modfield = 0;
121 | xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
122 | xcb_get_modifier_mapping_reply_t *reply = NULL;
123 | xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
124 |
125 | if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
126 | (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
127 | reply->keycodes_per_modifier < 1 ||
128 | (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
129 | goto end;
130 | }
131 |
132 | unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
133 | for (unsigned int i = 0; i < num_mod; i++) {
134 | for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
135 | xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
136 | if (mk == XCB_NO_SYMBOL) {
137 | continue;
138 | }
139 | for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
140 | if (*k == mk) {
141 | modfield |= (1 << i);
142 | }
143 | }
144 | }
145 | }
146 |
147 | end:
148 | xcb_key_symbols_free(symbols);
149 | free(keycodes);
150 | free(reply);
151 | return modfield;
152 | }
153 |
154 | resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
155 | {
156 | resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
157 | xcb_rectangle_t rect = get_rectangle(NULL, NULL, n);
158 | if (pac == ACTION_RESIZE_SIDE) {
159 | float W = rect.width;
160 | float H = rect.height;
161 | float ratio = W / H;
162 | float x = pos.x - rect.x;
163 | float y = pos.y - rect.y;
164 | float diag_a = ratio * y;
165 | float diag_b = W - diag_a;
166 | if (x < diag_a) {
167 | if (x < diag_b) {
168 | rh = HANDLE_LEFT;
169 | } else {
170 | rh = HANDLE_BOTTOM;
171 | }
172 | } else {
173 | if (x < diag_b) {
174 | rh = HANDLE_TOP;
175 | } else {
176 | rh = HANDLE_RIGHT;
177 | }
178 | }
179 | } else if (pac == ACTION_RESIZE_CORNER) {
180 | int16_t mid_x = rect.x + (rect.width / 2);
181 | int16_t mid_y = rect.y + (rect.height / 2);
182 | if (pos.x > mid_x) {
183 | if (pos.y > mid_y) {
184 | rh = HANDLE_BOTTOM_RIGHT;
185 | } else {
186 | rh = HANDLE_TOP_RIGHT;
187 | }
188 | } else {
189 | if (pos.y > mid_y) {
190 | rh = HANDLE_BOTTOM_LEFT;
191 | } else {
192 | rh = HANDLE_TOP_LEFT;
193 | }
194 | }
195 | }
196 | return rh;
197 | }
198 |
199 | bool grab_pointer(pointer_action_t pac)
200 | {
201 | xcb_window_t win = XCB_NONE;
202 | xcb_point_t pos;
203 |
204 | query_pointer(&win, &pos);
205 |
206 | coordinates_t loc;
207 |
208 | if (!locate_window(win, &loc)) {
209 | if (pac == ACTION_FOCUS) {
210 | monitor_t *m = monitor_from_point(pos);
211 | if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
212 | focus_node(m, m->desk, m->desk->focus);
213 | return true;
214 | }
215 | }
216 | return false;
217 | }
218 |
219 | if (pac == ACTION_FOCUS) {
220 | if (loc.node != mon->desk->focus) {
221 | focus_node(loc.monitor, loc.desktop, loc.node);
222 | return true;
223 | } else if (focus_follows_pointer) {
224 | stack(loc.desktop, loc.node, true);
225 | }
226 | return false;
227 | }
228 |
229 | if (loc.node->client->state == STATE_FULLSCREEN) {
230 | return true;
231 | }
232 |
233 | 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);
234 |
235 | if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
236 | free(reply);
237 | return true;
238 | }
239 | free(reply);
240 |
241 | if (pac == ACTION_MOVE) {
242 | 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);
243 | } else if (pac == ACTION_RESIZE_CORNER) {
244 | 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);
245 | } else if (pac == ACTION_RESIZE_SIDE) {
246 | 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);
247 | }
248 |
249 | track_pointer(loc, pac, pos);
250 |
251 | return true;
252 | }
253 |
254 | void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
255 | {
256 | node_t *n = loc.node;
257 | resize_handle_t rh = get_handle(loc.node, pos, pac);
258 |
259 | uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
260 | xcb_timestamp_t last_motion_time = 0;
261 |
262 | xcb_generic_event_t *evt = NULL;
263 |
264 | grabbing = true;
265 | grabbed_node = n;
266 |
267 | do {
268 | free(evt);
269 | while ((evt = xcb_wait_for_event(dpy)) == NULL) {
270 | xcb_flush(dpy);
271 | }
272 | uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
273 | if (resp_type == XCB_MOTION_NOTIFY) {
274 | xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
275 | uint32_t dtime = e->time - last_motion_time;
276 | if (dtime < pointer_motion_interval) {
277 | continue;
278 | }
279 | last_motion_time = e->time;
280 | int16_t dx = e->root_x - last_motion_x;
281 | int16_t dy = e->root_y - last_motion_y;
282 | if (pac == ACTION_MOVE) {
283 | move_client(&loc, dx, dy);
284 | } else {
285 | if (honor_size_hints) {
286 | resize_client(&loc, rh, e->root_x, e->root_y, false);
287 | } else {
288 | resize_client(&loc, rh, dx, dy, true);
289 | }
290 | }
291 | last_motion_x = e->root_x;
292 | last_motion_y = e->root_y;
293 | xcb_flush(dpy);
294 | } else if (resp_type == XCB_BUTTON_RELEASE) {
295 | grabbing = false;
296 | } else {
297 | handle_event(evt);
298 | }
299 | } while (grabbing && grabbed_node != NULL);
300 | free(evt);
301 |
302 | xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
303 |
304 | if (grabbed_node == NULL) {
305 | grabbing = false;
306 | return;
307 | }
308 |
309 | if (pac == ACTION_MOVE) {
310 | 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);
311 | } else if (pac == ACTION_RESIZE_CORNER) {
312 | 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);
313 | } else if (pac == ACTION_RESIZE_SIDE) {
314 | 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);
315 | }
316 |
317 | xcb_rectangle_t r = get_rectangle(NULL, NULL, n);
318 |
319 | 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);
320 |
321 | if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
322 | ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
323 | n->client->state == STATE_TILED)) {
324 | for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
325 | if (f == n || f->client == NULL || !IS_TILED(f->client)) {
326 | continue;
327 | }
328 | xcb_rectangle_t r = f->client->tiled_rectangle;
329 | 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);
330 | }
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/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 | uint16_t num_lock;
33 | uint16_t caps_lock;
34 | uint16_t scroll_lock;
35 |
36 | bool grabbing;
37 | 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_tree(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 | int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
60 | 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);
61 | int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);
62 | int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);
63 | void fprint_monitor_id(monitor_t *m, FILE *rsp);
64 | void fprint_monitor_name(monitor_t *m, FILE *rsp);
65 | void fprint_desktop_id(desktop_t *d, FILE *rsp);
66 | void fprint_desktop_name(desktop_t *d, FILE *rsp);
67 | void print_ignore_request(state_transition_t st, FILE *rsp);
68 | void print_modifier_mask(uint16_t m, FILE *rsp);
69 | void print_button_index(int8_t b, FILE *rsp);
70 | void print_pointer_action(pointer_action_t a, FILE *rsp);
71 | void print_rule_consequence(char **buf, rule_consequence_t *csq);
72 | void print_rectangle(char **buf, xcb_rectangle_t *rect);
73 | node_select_t make_node_select(void);
74 | desktop_select_t make_desktop_select(void);
75 | monitor_select_t make_monitor_select(void);
76 | int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
77 | int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
78 | int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
79 | bool locate_window(xcb_window_t win, coordinates_t *loc);
80 | bool locate_desktop(char *name, coordinates_t *loc);
81 | bool locate_monitor(char *name, coordinates_t *loc);
82 | bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);
83 | bool desktop_from_name(char *name, coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel, int *hits);
84 | bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);
85 | bool monitor_from_id(uint32_t id, coordinates_t *loc);
86 | bool monitor_from_index(int idx, coordinates_t *loc);
87 | bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t *sel);
88 | bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t *sel);
89 | bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t *sel);
90 |
91 | #endif
92 |
--------------------------------------------------------------------------------
/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_tree(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_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
41 | void restore_stack(jsmntok_t **t, char *json);
42 | bool keyeq(char *s, jsmntok_t *key, char *json);
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/src/rule.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 "ewmh.h"
33 | #include "window.h"
34 | #include "query.h"
35 | #include "parse.h"
36 | #include "settings.h"
37 | #include "rule.h"
38 |
39 | rule_t *make_rule(void)
40 | {
41 | rule_t *r = calloc(1, sizeof(rule_t));
42 | r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
43 | r->next = r->prev = NULL;
44 | r->one_shot = false;
45 | return r;
46 | }
47 |
48 | void add_rule(rule_t *r)
49 | {
50 | if (rule_head == NULL) {
51 | rule_head = rule_tail = r;
52 | } else {
53 | rule_tail->next = r;
54 | r->prev = rule_tail;
55 | rule_tail = r;
56 | }
57 | }
58 |
59 | void remove_rule(rule_t *r)
60 | {
61 | if (r == NULL) {
62 | return;
63 | }
64 | rule_t *prev = r->prev;
65 | rule_t *next = r->next;
66 | if (prev != NULL) {
67 | prev->next = next;
68 | }
69 | if (next != NULL) {
70 | next->prev = prev;
71 | }
72 | if (r == rule_head) {
73 | rule_head = next;
74 | }
75 | if (r == rule_tail) {
76 | rule_tail = prev;
77 | }
78 | free(r);
79 | }
80 |
81 | void remove_rule_by_cause(char *cause)
82 | {
83 | rule_t *r = rule_head;
84 | char *class_name = strtok(cause, COL_TOK);
85 | char *instance_name = strtok(NULL, COL_TOK);
86 | while (r != NULL) {
87 | rule_t *next = r->next;
88 | if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
89 | (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
90 | remove_rule(r);
91 | }
92 | r = next;
93 | }
94 | }
95 |
96 | bool remove_rule_by_index(int idx)
97 | {
98 | for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
99 | if (idx == 0) {
100 | remove_rule(r);
101 | return true;
102 | }
103 | }
104 | return false;
105 | }
106 |
107 | rule_consequence_t *make_rule_conquence(void)
108 | {
109 | rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
110 | rc->manage = rc->focus = rc->border = true;
111 | rc->layer = NULL;
112 | rc->state = NULL;
113 | rc->rect = NULL;
114 | return rc;
115 | }
116 |
117 | pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
118 | {
119 | pending_rule_t *pr = calloc(1, sizeof(pending_rule_t));
120 | pr->prev = pr->next = NULL;
121 | pr->event_head = pr->event_tail = NULL;
122 | pr->fd = fd;
123 | pr->win = win;
124 | pr->csq = csq;
125 | return pr;
126 | }
127 |
128 | void add_pending_rule(pending_rule_t *pr)
129 | {
130 | if (pr == NULL) {
131 | return;
132 | }
133 | if (pending_rule_head == NULL) {
134 | pending_rule_head = pending_rule_tail = pr;
135 | } else {
136 | pending_rule_tail->next = pr;
137 | pr->prev = pending_rule_tail;
138 | pending_rule_tail = pr;
139 | }
140 | }
141 |
142 | void remove_pending_rule(pending_rule_t *pr)
143 | {
144 | if (pr == NULL) {
145 | return;
146 | }
147 | pending_rule_t *a = pr->prev;
148 | pending_rule_t *b = pr->next;
149 | if (a != NULL) {
150 | a->next = b;
151 | }
152 | if (b != NULL) {
153 | b->prev = a;
154 | }
155 | if (pr == pending_rule_head) {
156 | pending_rule_head = b;
157 | }
158 | if (pr == pending_rule_tail) {
159 | pending_rule_tail = a;
160 | }
161 | close(pr->fd);
162 | free(pr->csq);
163 | event_queue_t *eq = pr->event_head;
164 | while (eq != NULL) {
165 | event_queue_t *next = eq->next;
166 | free(eq);
167 | eq = next;
168 | }
169 | free(pr);
170 | }
171 |
172 | void postpone_event(pending_rule_t *pr, xcb_generic_event_t *evt)
173 | {
174 | event_queue_t *eq = make_event_queue(evt);
175 | if (pr->event_tail == NULL) {
176 | pr->event_head = pr->event_tail = eq;
177 | } else {
178 | pr->event_tail->next = eq;
179 | eq->prev = pr->event_tail;
180 | pr->event_tail = eq;
181 | }
182 | }
183 |
184 | event_queue_t *make_event_queue(xcb_generic_event_t *evt)
185 | {
186 | event_queue_t *eq = calloc(1, sizeof(event_queue_t));
187 | eq->prev = eq->next = NULL;
188 | eq->event = *evt;
189 | return eq;
190 | }
191 |
192 |
193 | #define SET_CSQ_STATE(val) \
194 | do { \
195 | if (csq->state == NULL) { \
196 | csq->state = calloc(1, sizeof(client_state_t)); \
197 | } \
198 | *(csq->state) = (val); \
199 | } while (0)
200 |
201 | #define SET_CSQ_LAYER(val) \
202 | do { \
203 | if (csq->layer == NULL) { \
204 | csq->layer = calloc(1, sizeof(stack_layer_t)); \
205 | } \
206 | *(csq->layer) = (val); \
207 | } while (0)
208 |
209 | void _apply_window_type(xcb_window_t win, rule_consequence_t *csq)
210 | {
211 | xcb_ewmh_get_atoms_reply_t win_type;
212 | if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
213 | for (unsigned int i = 0; i < win_type.atoms_len; i++) {
214 | xcb_atom_t a = win_type.atoms[i];
215 | if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||
216 | a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
217 | csq->focus = false;
218 | } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
219 | SET_CSQ_STATE(STATE_FLOATING);
220 | csq->center = true;
221 | } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||
222 | a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||
223 | a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
224 | csq->manage = false;
225 | if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {
226 | window_lower(win);
227 | }
228 | }
229 | }
230 | xcb_ewmh_get_atoms_reply_wipe(&win_type);
231 | }
232 | }
233 |
234 | void _apply_window_state(xcb_window_t win, rule_consequence_t *csq)
235 | {
236 | xcb_ewmh_get_atoms_reply_t win_state;
237 | if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
238 | for (unsigned int i = 0; i < win_state.atoms_len; i++) {
239 | xcb_atom_t a = win_state.atoms[i];
240 | if (a == ewmh->_NET_WM_STATE_FULLSCREEN) {
241 | SET_CSQ_STATE(STATE_FULLSCREEN);
242 | } else if (a == ewmh->_NET_WM_STATE_BELOW) {
243 | SET_CSQ_LAYER(LAYER_BELOW);
244 | } else if (a == ewmh->_NET_WM_STATE_ABOVE) {
245 | SET_CSQ_LAYER(LAYER_ABOVE);
246 | } else if (a == ewmh->_NET_WM_STATE_STICKY) {
247 | csq->sticky = true;
248 | }
249 | }
250 | xcb_ewmh_get_atoms_reply_wipe(&win_state);
251 | }
252 | }
253 |
254 | void _apply_transient(xcb_window_t win, rule_consequence_t *csq)
255 | {
256 | xcb_window_t transient_for = XCB_NONE;
257 | xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
258 | if (transient_for != XCB_NONE) {
259 | SET_CSQ_STATE(STATE_FLOATING);
260 | }
261 | }
262 |
263 | void _apply_hints(xcb_window_t win, rule_consequence_t *csq)
264 | {
265 | xcb_size_hints_t size_hints;
266 | if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
267 | if ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&
268 | size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {
269 | SET_CSQ_STATE(STATE_FLOATING);
270 | }
271 | }
272 | }
273 |
274 | void _apply_class(xcb_window_t win, rule_consequence_t *csq)
275 | {
276 | xcb_icccm_get_wm_class_reply_t reply;
277 | if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
278 | snprintf(csq->class_name, sizeof(csq->class_name), "%s", reply.class_name);
279 | snprintf(csq->instance_name, sizeof(csq->instance_name), "%s", reply.instance_name);
280 | xcb_icccm_get_wm_class_reply_wipe(&reply);
281 | }
282 | }
283 |
284 | void parse_keys_values(char *buf, rule_consequence_t *csq)
285 | {
286 | char *key = strtok(buf, CSQ_BLK);
287 | char *value = strtok(NULL, CSQ_BLK);
288 | while (key != NULL && value != NULL) {
289 | parse_key_value(key, value, csq);
290 | key = strtok(NULL, CSQ_BLK);
291 | value = strtok(NULL, CSQ_BLK);
292 | }
293 | }
294 |
295 | void apply_rules(xcb_window_t win, rule_consequence_t *csq)
296 | {
297 | _apply_window_type(win, csq);
298 | _apply_window_state(win, csq);
299 | _apply_transient(win, csq);
300 | _apply_hints(win, csq);
301 | _apply_class(win, csq);
302 |
303 | rule_t *rule = rule_head;
304 | while (rule != NULL) {
305 | rule_t *next = rule->next;
306 | if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
307 | (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
308 | char effect[MAXLEN];
309 | snprintf(effect, sizeof(effect), "%s", rule->effect);
310 | parse_keys_values(effect, csq);
311 | if (rule->one_shot) {
312 | remove_rule(rule);
313 | break;
314 | }
315 | }
316 | rule = next;
317 | }
318 | }
319 |
320 | bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
321 | {
322 | if (external_rules_command[0] == '\0') {
323 | return false;
324 | }
325 | int fds[2];
326 | if (pipe(fds) == -1) {
327 | return false;
328 | }
329 | pid_t pid = fork();
330 | if (pid == 0) {
331 | if (dpy != NULL) {
332 | close(xcb_get_file_descriptor(dpy));
333 | }
334 | dup2(fds[1], 1);
335 | close(fds[0]);
336 | char wid[SMALEN];
337 | char *csq_buf;
338 | print_rule_consequence(&csq_buf, csq);
339 | snprintf(wid, sizeof(wid), "%i", win);
340 | setsid();
341 | execl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq_buf, NULL);
342 | free(csq_buf);
343 | err("Couldn't spawn rule command.\n");
344 | } else if (pid > 0) {
345 | close(fds[1]);
346 | pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
347 | add_pending_rule(pr);
348 | }
349 | return (pid != -1);
350 | }
351 |
352 | void parse_rule_consequence(int fd, rule_consequence_t *csq)
353 | {
354 | if (fd == -1) {
355 | return;
356 | }
357 | char data[BUFSIZ];
358 | int nb;
359 | while ((nb = read(fd, data, sizeof(data))) > 0) {
360 | int end = MIN(nb, (int) sizeof(data) - 1);
361 | data[end] = '\0';
362 | parse_keys_values(data, csq);
363 | }
364 | }
365 |
366 | void parse_key_value(char *key, char *value, rule_consequence_t *csq)
367 | {
368 | bool v;
369 | if (streq("monitor", key)) {
370 | snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
371 | } else if (streq("desktop", key)) {
372 | snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
373 | } else if (streq("node", key)) {
374 | snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
375 | } else if (streq("split_dir", key)) {
376 | snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
377 | } else if (streq("state", key)) {
378 | client_state_t cst;
379 | if (parse_client_state(value, &cst)) {
380 | SET_CSQ_STATE(cst);
381 | }
382 | } else if (streq("layer", key)) {
383 | stack_layer_t lyr;
384 | if (parse_stack_layer(value, &lyr)) {
385 | SET_CSQ_LAYER(lyr);
386 | }
387 | } else if (streq("split_ratio", key)) {
388 | double rat;
389 | if (sscanf(value, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
390 | csq->split_ratio = rat;
391 | }
392 | } else if (streq("rectangle", key)) {
393 | if (csq->rect == NULL) {
394 | csq->rect = calloc(1, sizeof(xcb_rectangle_t));
395 | }
396 | if (!parse_rectangle(value, csq->rect)) {
397 | free(csq->rect);
398 | csq->rect = NULL;
399 | }
400 | } else if (parse_bool(value, &v)) {
401 | if (streq("hidden", key)) {
402 | csq->hidden = true;
403 | }
404 | #define SETCSQ(name) \
405 | else if (streq(#name, key)) { \
406 | csq->name = v; \
407 | }
408 | SETCSQ(sticky)
409 | SETCSQ(private)
410 | SETCSQ(locked)
411 | SETCSQ(marked)
412 | SETCSQ(center)
413 | SETCSQ(follow)
414 | SETCSQ(manage)
415 | SETCSQ(focus)
416 | SETCSQ(border)
417 | #undef SETCSQ
418 | }
419 | }
420 |
421 | #undef SET_CSQ_LAYER
422 | #undef SET_CSQ_STATE
423 |
424 | void list_rules(FILE *rsp)
425 | {
426 | for (rule_t *r = rule_head; r != NULL; r = r->next) {
427 | fprintf(rsp, "%s:%s %c> %s\n", r->class_name, r->instance_name, r->one_shot?'-':'=', r->effect);
428 | }
429 | }
430 |
--------------------------------------------------------------------------------
/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_conquence(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 parse_keys_values(char *buf, rule_consequence_t *csq);
48 | void apply_rules(xcb_window_t win, rule_consequence_t *csq);
49 | bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
50 | void parse_rule_consequence(int fd, rule_consequence_t *csq);
51 | void parse_key_value(char *key, char *value, rule_consequence_t *csq);
52 | void list_rules(FILE *rsp);
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/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 | extern char **environ;
32 |
33 | void run_config(void)
34 | {
35 | if (fork() == 0) {
36 | if (dpy != NULL) {
37 | close(xcb_get_file_descriptor(dpy));
38 | }
39 | setsid();
40 | execle(config_path, config_path, (char *) NULL, environ);
41 | err("Couldn't execute the configuration file.\n");
42 | }
43 | }
44 |
45 | void load_settings(void)
46 | {
47 | snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND);
48 | snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
49 |
50 | snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
51 | snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
52 | snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
53 | snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
54 |
55 | padding = (padding_t) PADDING;
56 | window_gap = WINDOW_GAP;
57 | border_width = BORDER_WIDTH;
58 | border_radius = BORDER_RADIUS;
59 | split_ratio = SPLIT_RATIO;
60 | initial_polarity = SECOND_CHILD;
61 | automatic_scheme = SCHEME_LONGEST_SIDE;
62 | directional_focus_tightness = TIGHTNESS_HIGH;
63 |
64 | pointer_modifier = POINTER_MODIFIER;
65 | pointer_motion_interval = POINTER_MOTION_INTERVAL;
66 | pointer_actions[0] = ACTION_MOVE;
67 | pointer_actions[1] = ACTION_RESIZE_SIDE;
68 | pointer_actions[2] = ACTION_RESIZE_CORNER;
69 | mapping_events_count = MAPPING_EVENTS_COUNT;
70 |
71 | borderless_monocle = BORDERLESS_MONOCLE;
72 | gapless_monocle = GAPLESS_MONOCLE;
73 | paddingless_monocle = PADDINGLESS_MONOCLE;
74 | single_monocle = SINGLE_MONOCLE;
75 |
76 | focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
77 | pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
78 | pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
79 | click_to_focus = CLICK_TO_FOCUS;
80 | swallow_first_click = SWALLOW_FIRST_CLICK;
81 | ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
82 | ignore_ewmh_fullscreen = IGNORE_EWMH_FULLSCREEN;
83 | send_ewmh_fullscreen = SEND_EWMH_FULLSCREEN;
84 |
85 | center_pseudo_tiled = CENTER_PSEUDO_TILED;
86 | honor_size_hints = HONOR_SIZE_HINTS;
87 |
88 | remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
89 | remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
90 | merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
91 | }
92 |
--------------------------------------------------------------------------------
/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 WINDOW_GAP 6
42 | #define BORDER_WIDTH 1
43 | #define BORDER_RADIUS 0
44 | #define SPLIT_RATIO 0.5
45 | #define AUTOMATIC_SCHEME SCHEME_LONGEST_SIDE
46 |
47 | #define BORDERLESS_MONOCLE false
48 | #define GAPLESS_MONOCLE false
49 | #define PADDINGLESS_MONOCLE false
50 | #define SINGLE_MONOCLE false
51 |
52 | #define FOCUS_FOLLOWS_POINTER false
53 | #define POINTER_FOLLOWS_FOCUS false
54 | #define POINTER_FOLLOWS_MONITOR false
55 | #define CLICK_TO_FOCUS XCB_BUTTON_INDEX_1
56 | #define SWALLOW_FIRST_CLICK false
57 | #define IGNORE_EWMH_FOCUS false
58 | #define IGNORE_EWMH_FULLSCREEN 0
59 | #define SEND_EWMH_FULLSCREEN 0
60 |
61 | #define CENTER_PSEUDO_TILED true
62 | #define HONOR_SIZE_HINTS false
63 | #define MAPPING_EVENTS_COUNT 1
64 |
65 | #define REMOVE_DISABLED_MONITORS false
66 | #define REMOVE_UNPLUGGED_MONITORS false
67 | #define MERGE_OVERLAPPING_MONITORS false
68 |
69 | char external_rules_command[MAXLEN];
70 | char status_prefix[MAXLEN];
71 |
72 | char normal_border_color[MAXLEN];
73 | char active_border_color[MAXLEN];
74 | char focused_border_color[MAXLEN];
75 | char presel_feedback_color[MAXLEN];
76 |
77 | padding_t padding;
78 | int window_gap;
79 | unsigned int border_width;
80 | unsigned int border_radius;
81 | double split_ratio;
82 | child_polarity_t initial_polarity;
83 | automatic_scheme_t automatic_scheme;
84 | tightness_t directional_focus_tightness;
85 |
86 | uint16_t pointer_modifier;
87 | uint32_t pointer_motion_interval;
88 | pointer_action_t pointer_actions[3];
89 | int8_t mapping_events_count;
90 |
91 | bool borderless_monocle;
92 | bool gapless_monocle;
93 | bool paddingless_monocle;
94 | bool single_monocle;
95 |
96 | bool focus_follows_pointer;
97 | bool pointer_follows_focus;
98 | bool pointer_follows_monitor;
99 | int8_t click_to_focus;
100 | bool swallow_first_click;
101 | bool ignore_ewmh_focus;
102 | state_transition_t ignore_ewmh_fullscreen;
103 | state_transition_t send_ewmh_fullscreen;
104 |
105 | bool center_pseudo_tiled;
106 | bool honor_size_hints;
107 |
108 | bool remove_disabled_monitors;
109 | bool remove_unplugged_monitors;
110 | bool merge_overlapping_monitors;
111 |
112 | void run_config(void);
113 | void load_settings(void);
114 |
115 | #endif
116 |
--------------------------------------------------------------------------------
/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) ? 2 : 1));
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 "bspwm.h"
31 | #include "desktop.h"
32 | #include "settings.h"
33 | #include "subscribe.h"
34 |
35 | subscriber_list_t *make_subscriber_list(FILE *stream, char *fifo_path, int field, int count)
36 | {
37 | subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));
38 | sb->prev = sb->next = NULL;
39 | sb->stream = stream;
40 | sb->fifo_path = fifo_path;
41 | sb->field = field;
42 | sb->count = count;
43 | return sb;
44 | }
45 |
46 | void remove_subscriber(subscriber_list_t *sb)
47 | {
48 | if (sb == NULL) {
49 | return;
50 | }
51 | subscriber_list_t *a = sb->prev;
52 | subscriber_list_t *b = sb->next;
53 | if (a != NULL) {
54 | a->next = b;
55 | }
56 | if (b != NULL) {
57 | b->prev = a;
58 | }
59 | if (sb == subscribe_head) {
60 | subscribe_head = b;
61 | }
62 | if (sb == subscribe_tail) {
63 | subscribe_tail = a;
64 | }
65 | fclose(sb->stream);
66 | unlink(sb->fifo_path);
67 | free(sb->fifo_path);
68 | free(sb);
69 | }
70 |
71 | void add_subscriber(FILE *stream, char* fifo_path, int field, int count)
72 | {
73 | subscriber_list_t *sb = make_subscriber_list(stream, fifo_path, field, count);
74 | if (subscribe_head == NULL) {
75 | subscribe_head = subscribe_tail = sb;
76 | } else {
77 | subscribe_tail->next = sb;
78 | sb->prev = subscribe_tail;
79 | subscribe_tail = sb;
80 | }
81 | if (sb->field & SBSC_MASK_REPORT) {
82 | print_report(sb->stream);
83 | if (sb->count-- == 1) {
84 | remove_subscriber(sb);
85 | }
86 | }
87 | }
88 |
89 | int print_report(FILE *stream)
90 | {
91 | fprintf(stream, "%s", status_prefix);
92 | for (monitor_t *m = mon_head; m != NULL; m = m->next) {
93 | fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
94 | for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
95 | char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));
96 | if (m->desk == d) {
97 | c = toupper(c);
98 | }
99 | fprintf(stream, ":%c%s", c, d->name);
100 | }
101 | if (m->desk != NULL) {
102 | fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
103 | if (m->desk->focus != NULL) {
104 | node_t *n = m->desk->focus;
105 | if (n->client != NULL) {
106 | fprintf(stream, ":T%c", STATE_CHR(n->client->state));
107 | } else {
108 | fprintf(stream, ":T@");
109 | }
110 | int i = 0;
111 | char flags[5];
112 | if (n->sticky) {
113 | flags[i++] = 'S';
114 | }
115 | if (n->private) {
116 | flags[i++] = 'P';
117 | }
118 | if (n->locked) {
119 | flags[i++] = 'L';
120 | }
121 | if (n->marked) {
122 | flags[i++] = 'M';
123 | }
124 | flags[i] = '\0';
125 | fprintf(stream, ":G%s", flags);
126 | }
127 | }
128 | if (m != mon_tail) {
129 | fprintf(stream, "%s", ":");
130 | }
131 | }
132 | fprintf(stream, "%s", "\n");
133 | return fflush(stream);
134 | }
135 |
136 | void put_status(subscriber_mask_t mask, ...)
137 | {
138 | subscriber_list_t *sb = subscribe_head;
139 | int ret;
140 | while (sb != NULL) {
141 | subscriber_list_t *next = sb->next;
142 | if (sb->field & mask) {
143 | if (sb->count > 0) {
144 | sb->count--;
145 | }
146 | if (mask == SBSC_MASK_REPORT) {
147 | ret = print_report(sb->stream);
148 | } else {
149 | char *fmt;
150 | va_list args;
151 | va_start(args, mask);
152 | fmt = va_arg(args, char *);
153 | vfprintf(sb->stream, fmt, args);
154 | va_end(args);
155 | ret = fflush(sb->stream);
156 | }
157 | if (ret != 0 || sb->count == 0) {
158 | remove_subscriber(sb);
159 | }
160 | }
161 | sb = next;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/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 << 28) - (1 << 15),
62 | SBSC_MASK_ALL = (1 << 28) - 1
63 | } subscriber_mask_t;
64 |
65 | subscriber_list_t *make_subscriber_list(FILE *stream, char *fifo_path, int field, int count);
66 | void remove_subscriber(subscriber_list_t *sb);
67 | void add_subscriber(FILE *stream, char* fifo_path, int field, int count);
68 | int print_report(FILE *stream);
69 | void put_status(subscriber_mask_t mask, ...);
70 |
71 | #endif
72 |
--------------------------------------------------------------------------------
/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, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
33 | presel_t *make_presel(void);
34 | void set_ratio(node_t *n, double rat);
35 | void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
36 | void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
37 | void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
38 | void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);
39 | node_t *find_public(desktop_t *d);
40 | node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
41 | void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);
42 | bool activate_node(monitor_t *m, desktop_t *d, node_t *n);
43 | void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
44 | bool focus_node(monitor_t *m, desktop_t *d, node_t *n);
45 | void hide_node(desktop_t *d, node_t *n);
46 | void show_node(desktop_t *d, node_t *n);
47 | node_t *make_node(uint32_t id);
48 | client_t *make_client(void);
49 | void initialize_client(node_t *n);
50 | bool is_focusable(node_t *n);
51 | bool is_leaf(node_t *n);
52 | bool is_first_child(node_t *n);
53 | bool is_second_child(node_t *n);
54 | unsigned int clients_count_in(node_t *n);
55 | node_t *brother_tree(node_t *n);
56 | node_t *first_extrema(node_t *n);
57 | node_t *second_extrema(node_t *n);
58 | node_t *first_focusable_leaf(node_t *n);
59 | node_t *next_leaf(node_t *n, node_t *r);
60 | node_t *prev_leaf(node_t *n, node_t *r);
61 | node_t *next_tiled_leaf(node_t *n, node_t *r);
62 | node_t *prev_tiled_leaf(node_t *n, node_t *r);
63 | bool is_adjacent(node_t *a, node_t *b, direction_t dir);
64 | node_t *find_fence(node_t *n, direction_t dir);
65 | bool is_child(node_t *a, node_t *b);
66 | bool is_descendant(node_t *a, node_t *b);
67 | bool find_by_id(uint32_t id, coordinates_t *loc);
68 | node_t *find_by_id_in(node_t *r, uint32_t id);
69 | void find_any_node(coordinates_t *ref, coordinates_t *dst, node_select_t *sel);
70 | bool find_any_node_in(monitor_t *m, desktop_t *d, node_t *n, coordinates_t *ref, coordinates_t *dst, node_select_t *sel);
71 | void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t *sel);
72 | unsigned int node_area(desktop_t *d, node_t *n);
73 | int tiled_count(node_t *n, bool include_receptacles);
74 | void find_by_area(area_peak_t ap, coordinates_t *ref, coordinates_t *dst, node_select_t *sel);
75 | void rotate_tree(node_t *n, int deg);
76 | void rotate_tree_rec(node_t *n, int deg);
77 | void flip_tree(node_t *n, flip_t flp);
78 | void equalize_tree(node_t *n);
79 | int balance_tree(node_t *n);
80 | void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
81 | void close_node(node_t *n);
82 | void kill_node(monitor_t *m, desktop_t *d, node_t *n);
83 | void remove_node(monitor_t *m, desktop_t *d, node_t *n);
84 | void free_node(node_t *n);
85 | bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2, bool follow);
86 | bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd, bool follow);
87 | bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t *sel);
88 | void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
89 | void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);
90 | void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
91 | void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
92 | void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);
93 | bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
94 | bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
95 | void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
96 | void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
97 | void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
98 | void rebuild_constraints(node_t *n);
99 | void update_constraints(node_t *n);
100 | void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);
101 | void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);
102 | void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
103 | void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
104 | void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);
105 | void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
106 | void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
107 | void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
108 | void set_marked(monitor_t *m, desktop_t *d, node_t *n, bool value);
109 | void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
110 | bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
111 | xcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n);
112 | void listen_enter_notify(node_t *n, bool enable);
113 | void regenerate_ids_in(node_t *n);
114 |
115 | unsigned int sticky_count(node_t *n);
116 | unsigned int private_count(node_t *n);
117 | unsigned int locked_count(node_t *n);
118 |
119 | #endif
120 |
--------------------------------------------------------------------------------
/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_SPIRAL
50 | } automatic_scheme_t;
51 |
52 | typedef enum {
53 | STATE_TILED,
54 | STATE_PSEUDO_TILED,
55 | STATE_FLOATING,
56 | STATE_FULLSCREEN
57 | } client_state_t;
58 |
59 | typedef enum {
60 | WM_FLAG_MODAL = 1 << 0,
61 | WM_FLAG_STICKY = 1 << 1,
62 | WM_FLAG_MAXIMIZED_VERT = 1 << 2,
63 | WM_FLAG_MAXIMIZED_HORZ = 1 << 3,
64 | WM_FLAG_SHADED = 1 << 4,
65 | WM_FLAG_SKIP_TASKBAR = 1 << 5,
66 | WM_FLAG_SKIP_PAGER = 1 << 6,
67 | WM_FLAG_HIDDEN = 1 << 7,
68 | WM_FLAG_FULLSCREEN = 1 << 8,
69 | WM_FLAG_ABOVE = 1 << 9,
70 | WM_FLAG_BELOW = 1 << 10,
71 | WM_FLAG_DEMANDS_ATTENTION = 1 << 11,
72 | } wm_flags_t;
73 |
74 | typedef enum {
75 | LAYER_BELOW,
76 | LAYER_NORMAL,
77 | LAYER_ABOVE
78 | } stack_layer_t;
79 |
80 | typedef enum {
81 | OPTION_NONE,
82 | OPTION_TRUE,
83 | OPTION_FALSE
84 | } option_bool_t;
85 |
86 | typedef enum {
87 | ALTER_TOGGLE,
88 | ALTER_SET
89 | } alter_state_t;
90 |
91 | typedef enum {
92 | CYCLE_NEXT,
93 | CYCLE_PREV
94 | } cycle_dir_t;
95 |
96 | typedef enum {
97 | CIRCULATE_FORWARD,
98 | CIRCULATE_BACKWARD
99 | } circulate_dir_t;
100 |
101 | typedef enum {
102 | HISTORY_OLDER,
103 | HISTORY_NEWER
104 | } history_dir_t;
105 |
106 | typedef enum {
107 | DIR_NORTH,
108 | DIR_WEST,
109 | DIR_SOUTH,
110 | DIR_EAST
111 | } direction_t;
112 |
113 | typedef enum {
114 | HANDLE_LEFT = 1 << 0,
115 | HANDLE_TOP = 1 << 1,
116 | HANDLE_RIGHT = 1 << 2,
117 | HANDLE_BOTTOM = 1 << 3,
118 | HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,
119 | HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,
120 | HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,
121 | HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT
122 | } resize_handle_t;
123 |
124 | typedef enum {
125 | ACTION_NONE,
126 | ACTION_FOCUS,
127 | ACTION_MOVE,
128 | ACTION_RESIZE_SIDE,
129 | ACTION_RESIZE_CORNER
130 | } pointer_action_t;
131 |
132 | typedef enum {
133 | LAYOUT_TILED,
134 | LAYOUT_MONOCLE
135 | } layout_t;
136 |
137 | typedef enum {
138 | FLIP_HORIZONTAL,
139 | FLIP_VERTICAL
140 | } flip_t;
141 |
142 | typedef enum {
143 | FIRST_CHILD,
144 | SECOND_CHILD
145 | } child_polarity_t;
146 |
147 | typedef enum {
148 | TIGHTNESS_LOW,
149 | TIGHTNESS_HIGH,
150 | } tightness_t;
151 |
152 | typedef enum {
153 | AREA_BIGGEST,
154 | AREA_SMALLEST,
155 | } area_peak_t;
156 |
157 | typedef enum {
158 | STATE_TRANSITION_ENTER = 1 << 0,
159 | STATE_TRANSITION_EXIT = 1 << 1,
160 | } state_transition_t;
161 |
162 | typedef struct {
163 | option_bool_t automatic;
164 | option_bool_t focused;
165 | option_bool_t local;
166 | option_bool_t active;
167 | option_bool_t leaf;
168 | option_bool_t window;
169 | option_bool_t tiled;
170 | option_bool_t pseudo_tiled;
171 | option_bool_t floating;
172 | option_bool_t fullscreen;
173 | option_bool_t hidden;
174 | option_bool_t sticky;
175 | option_bool_t private;
176 | option_bool_t locked;
177 | option_bool_t marked;
178 | option_bool_t urgent;
179 | option_bool_t same_class;
180 | option_bool_t descendant_of;
181 | option_bool_t ancestor_of;
182 | option_bool_t below;
183 | option_bool_t normal;
184 | option_bool_t above;
185 | } node_select_t;
186 |
187 | typedef struct {
188 | option_bool_t occupied;
189 | option_bool_t focused;
190 | option_bool_t urgent;
191 | option_bool_t local;
192 | } desktop_select_t;
193 |
194 | typedef struct {
195 | option_bool_t occupied;
196 | option_bool_t focused;
197 | } monitor_select_t;
198 |
199 | typedef struct icccm_props_t icccm_props_t;
200 | struct icccm_props_t {
201 | bool take_focus;
202 | bool input_hint;
203 | bool delete_window;
204 | };
205 |
206 | typedef struct {
207 | char class_name[3 * SMALEN / 2];
208 | char instance_name[3 * SMALEN / 2];
209 | unsigned int border_width;
210 | unsigned int border_radius;
211 | unsigned int drawn_border_radius;
212 | bool urgent;
213 | bool shown;
214 | bool sets_own_shape;
215 | client_state_t state;
216 | client_state_t last_state;
217 | stack_layer_t layer;
218 | stack_layer_t last_layer;
219 | xcb_rectangle_t floating_rectangle;
220 | xcb_rectangle_t tiled_rectangle;
221 | xcb_size_hints_t size_hints;
222 | icccm_props_t icccm_props;
223 | wm_flags_t wm_flags;
224 | } client_t;
225 |
226 | typedef struct presel_t presel_t;
227 | struct presel_t {
228 | double split_ratio;
229 | direction_t split_dir;
230 | xcb_window_t feedback;
231 | };
232 |
233 | typedef struct constraints_t constraints_t;
234 | struct constraints_t {
235 | uint16_t min_width;
236 | uint16_t min_height;
237 | };
238 |
239 | typedef struct node_t node_t;
240 | struct node_t {
241 | uint32_t id;
242 | split_type_t split_type;
243 | double split_ratio;
244 | presel_t *presel;
245 | xcb_rectangle_t rectangle;
246 | constraints_t constraints;
247 | bool vacant;
248 | bool hidden;
249 | bool sticky;
250 | bool private;
251 | bool locked;
252 | bool marked;
253 | node_t *first_child;
254 | node_t *second_child;
255 | node_t *parent;
256 | client_t *client;
257 | };
258 |
259 | typedef struct padding_t padding_t;
260 | struct padding_t {
261 | int top;
262 | int right;
263 | int bottom;
264 | int left;
265 | };
266 |
267 | typedef struct desktop_t desktop_t;
268 | struct desktop_t {
269 | char name[SMALEN];
270 | uint32_t id;
271 | layout_t layout;
272 | node_t *root;
273 | node_t *focus;
274 | desktop_t *prev;
275 | desktop_t *next;
276 | padding_t padding;
277 | int window_gap;
278 | unsigned int border_width;
279 | unsigned int border_radius;
280 | };
281 |
282 | typedef struct monitor_t monitor_t;
283 | struct monitor_t {
284 | char name[SMALEN];
285 | uint32_t id;
286 | xcb_randr_output_t randr_id;
287 | xcb_window_t root;
288 | bool wired;
289 | padding_t padding;
290 | unsigned int sticky_count;
291 | int window_gap;
292 | unsigned int border_width;
293 | unsigned int border_radius;
294 | xcb_rectangle_t rectangle;
295 | desktop_t *desk;
296 | desktop_t *desk_head;
297 | desktop_t *desk_tail;
298 | monitor_t *prev;
299 | monitor_t *next;
300 | };
301 |
302 | typedef struct {
303 | monitor_t *monitor;
304 | desktop_t *desktop;
305 | node_t *node;
306 | } coordinates_t;
307 |
308 | typedef struct history_t history_t;
309 | struct history_t {
310 | coordinates_t loc;
311 | bool latest;
312 | history_t *prev;
313 | history_t *next;
314 | };
315 |
316 | typedef struct stacking_list_t stacking_list_t;
317 | struct stacking_list_t {
318 | node_t *node;
319 | stacking_list_t *prev;
320 | stacking_list_t *next;
321 | };
322 |
323 | typedef struct event_queue_t event_queue_t;
324 | struct event_queue_t {
325 | xcb_generic_event_t event;
326 | event_queue_t *prev;
327 | event_queue_t *next;
328 | };
329 |
330 | typedef struct subscriber_list_t subscriber_list_t;
331 | struct subscriber_list_t {
332 | int fd;
333 | FILE *stream;
334 | char* fifo_path;
335 | int field;
336 | int count;
337 | subscriber_list_t *prev;
338 | subscriber_list_t *next;
339 | };
340 |
341 | typedef struct rule_t rule_t;
342 | struct rule_t {
343 | char class_name[MAXLEN];
344 | char instance_name[MAXLEN];
345 | char effect[MAXLEN];
346 | bool one_shot;
347 | rule_t *prev;
348 | rule_t *next;
349 | };
350 |
351 | typedef struct {
352 | char class_name[3 * SMALEN / 2];
353 | char instance_name[3 * SMALEN / 2];
354 | char monitor_desc[MAXLEN];
355 | char desktop_desc[MAXLEN];
356 | char node_desc[MAXLEN];
357 | char split_dir[SMALEN];
358 | double split_ratio;
359 | stack_layer_t *layer;
360 | client_state_t *state;
361 | bool hidden;
362 | bool sticky;
363 | bool private;
364 | bool locked;
365 | bool marked;
366 | bool center;
367 | bool follow;
368 | bool manage;
369 | bool focus;
370 | bool border;
371 | xcb_rectangle_t *rect;
372 | } rule_consequence_t;
373 |
374 | typedef struct pending_rule_t pending_rule_t;
375 | struct pending_rule_t {
376 | int fd;
377 | xcb_window_t win;
378 | rule_consequence_t *csq;
379 | event_queue_t *event_head;
380 | event_queue_t *event_tail;
381 | pending_rule_t *prev;
382 | pending_rule_t *next;
383 | };
384 |
385 | #endif
386 |
--------------------------------------------------------------------------------
/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_rounded_border(node_t *n);
48 | void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
49 | void adopt_orphans(void);
50 | uint32_t get_border_color(bool focused_node, bool focused_monitor);
51 | void initialize_floating_rectangle(node_t *n);
52 | xcb_rectangle_t get_window_rectangle(node_t *n);
53 | bool move_client(coordinates_t *loc, int dx, int dy);
54 | bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy, bool relative);
55 | void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);
56 | void query_pointer(xcb_window_t *win, xcb_point_t *pt);
57 | void update_motion_recorder(void);
58 | void enable_motion_recorder(xcb_window_t win);
59 | void disable_motion_recorder(void);
60 | void window_border_width(xcb_window_t win, uint32_t bw);
61 | void window_move(xcb_window_t win, int16_t x, int16_t y);
62 | void window_resize(xcb_window_t win, uint16_t w, uint16_t h);
63 | void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);
64 | void window_center(monitor_t *m, client_t *c);
65 | void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);
66 | void window_above(xcb_window_t w1, xcb_window_t w2);
67 | void window_below(xcb_window_t w1, xcb_window_t w2);
68 | void window_lower(xcb_window_t win);
69 | void window_set_visibility(xcb_window_t win, bool visible);
70 | void window_hide(xcb_window_t win);
71 | void window_show(xcb_window_t win);
72 | void update_input_focus(void);
73 | void set_input_focus(node_t *n);
74 | void clear_input_focus(void);
75 | void center_pointer(xcb_rectangle_t r);
76 | void get_atom(char *name, xcb_atom_t *atom);
77 | void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);
78 | void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);
79 | bool window_exists(xcb_window_t win);
80 |
81 | #endif
82 |
--------------------------------------------------------------------------------
/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 Test:test -o desktop="test-sticky-b"
16 |
17 | 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 cmd
14 | case "$action" in
15 | add) cmd=./test_window ;;
16 | remove) cmd="bspc node -c" ;;
17 | esac
18 | while [ $iter -gt 0 ] ; do
19 | local rsp_chan=$(bspc subscribe -f -c "$delta" "$event")
20 | $cmd &
21 | cat "$rsp_chan" > /dev/null
22 | iter=$((iter - 1))
23 | done
24 | }
25 |
--------------------------------------------------------------------------------
/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(void)
61 | {
62 | xcb_connection_t *dpy = xcb_connect(NULL, NULL);
63 | if (dpy == NULL) {
64 | fprintf(stderr, "Can't connect to X.\n");
65 | return EXIT_FAILURE;
66 | }
67 | xcb_atom_t WM_PROTOCOLS, WM_DELETE_WINDOW;
68 | if (!get_atom(dpy, "WM_PROTOCOLS", &WM_PROTOCOLS) ||
69 | !get_atom(dpy, "WM_DELETE_WINDOW", &WM_DELETE_WINDOW)) {
70 | fprintf(stderr, "Can't get required atoms.\n");
71 | xcb_disconnect(dpy);
72 | return EXIT_FAILURE;
73 | }
74 | xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
75 | if (screen == NULL) {
76 | fprintf(stderr, "Can't get current screen.\n");
77 | xcb_disconnect(dpy);
78 | return EXIT_FAILURE;
79 | }
80 | xcb_window_t win = xcb_generate_id(dpy);
81 | uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
82 | uint32_t values[] = {0xff111111, XCB_EVENT_MASK_EXPOSURE};
83 | xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, screen->root, 0, 0, 320, 240, 2,
84 | XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
85 | xcb_icccm_set_wm_class(dpy, win, sizeof(TEST_WINDOW_IC), TEST_WINDOW_IC);
86 | xcb_map_window(dpy, win);
87 | xcb_flush(dpy);
88 | xcb_generic_event_t *evt;
89 | bool running = true;
90 | while (running && (evt = xcb_wait_for_event(dpy)) != NULL) {
91 | uint8_t rt = XCB_EVENT_RESPONSE_TYPE(evt);
92 | if (rt == XCB_CLIENT_MESSAGE) {
93 | xcb_client_message_event_t *cme = (xcb_client_message_event_t *) evt;
94 | if (cme->type == WM_PROTOCOLS && cme->data.data32[0] == WM_DELETE_WINDOW) {
95 | running = false;
96 | }
97 | } else if (rt == XCB_EXPOSE) {
98 | render_text(dpy, win, 12, 24);
99 | }
100 | free(evt);
101 | }
102 | xcb_destroy_window(dpy, win);
103 | xcb_disconnect(dpy);
104 | return EXIT_SUCCESS;
105 | }
106 |
--------------------------------------------------------------------------------