Update upstream source from tag 'upstream/4.21'
Update to upstream version '4.21'
with Debian dir cc7789f614727be2ce7bc427728fdcdb3b9d1a73
Jakob Haufe
1 year, 7 months ago
40 | 40 | * Use `clang-format` to format your code. |
41 | 41 | * Run the [testsuite](https://i3wm.org/docs/testsuite.html) |
42 | 42 | * If your changes should be reported on the next release's changelog, also |
43 | update the [RELEASE-notes-next](../RELEASE-notes-next) file in the root | |
44 | folder. Example of changes that should be reported are bug fixes present in | |
45 | the latest stable version of i3 and new enhancements. Example of changes that | |
46 | should not be reported are minor code improvements, documentation, regression | |
47 | and fixes for bugs that were introduced in the `next` branch. | |
43 | add a small single-line file starting with a number (see examples) containing | |
44 | a short explanation of your change either in the | |
45 | [changes](../release-notes/changes) or the | |
46 | [bugfixes](../release-notes/bugfixes/) folder. Example of changes that should | |
47 | be reported are bug fixes present in the latest stable version of i3 and new | |
48 | enhancements. Example of changes that should not be reported are minor code | |
49 | improvements, documentation, regression and fixes for bugs that were | |
50 | introduced in the `next` branch. | |
48 | 51 | |
49 | 52 | ## Finding something to do |
50 | 53 |
44 | 44 | echo "::group::Ubuntu i386" |
45 | 45 | ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_UBUNTU_386 }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU_386 }} travis/travis-base-ubuntu-386.Dockerfile |
46 | 46 | echo "::endgroup::" |
47 | - name: verify safe wrapper functions are used | |
48 | run: | | |
49 | docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/check-safe-wrappers.sh | |
50 | - name: verify code formatting | |
51 | run: | | |
52 | docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/check-formatting.sh | |
53 | 47 | - name: build i3 |
54 | 48 | run: | |
55 | 49 | docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf build; mkdir -p build && cd build && CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common" meson .. -Ddocs=true -Dmans=true -Db_sanitize=address && ninja -v' |
91 | 85 | - name: push docs to GitHub pages |
92 | 86 | run: | |
93 | 87 | ./travis/skip-pkg.sh || travis/deploy-github-pages.sh |
88 | formatting: | |
89 | name: Check formatting | |
90 | runs-on: ubuntu-latest | |
91 | steps: | |
92 | - uses: actions/checkout@v2 | |
93 | - name: check & print release notes | |
94 | run: ./release-notes/generator.pl | |
95 | - name: Install dependencies | |
96 | run: | | |
97 | sudo apt-get install -y clang-format-10 | |
98 | - name: Check formatting | |
99 | run: clang-format-10 --dry-run --Werror $(git ls-files '*.c' '*.h') | |
100 | - name: Verify safe wrapper functions are used | |
101 | run: ./travis/check-safe-wrappers.sh |
0 | *.o | |
1 | tags | |
2 | include/GENERATED_*.h | |
3 | include/all.h.pch | |
4 | *~ | |
5 | *.swp | |
6 | *.gcda | |
7 | *.gcno | |
8 | *.dSYM | |
9 | test.commands_parser | |
10 | test.config_parser | |
11 | testcases/MYMETA.json | |
12 | testcases/MYMETA.yml | |
13 | testcases/blib/ | |
14 | testcases/pm_to_blib | |
15 | AnyEvent-I3/Makefile | |
16 | AnyEvent-I3/META.yml | |
17 | AnyEvent-I3/MYMETA.json | |
18 | AnyEvent-I3/MYMETA.yml | |
19 | AnyEvent-I3/blib/ | |
20 | AnyEvent-I3/inc/ | |
21 | AnyEvent-I3/pm_to_blib | |
22 | *.output | |
23 | *.tab.* | |
24 | *.yy.c | |
25 | man/*.1 | |
26 | man/*.xml | |
27 | man/*.html | |
28 | *.tar.bz2* | |
29 | i3 | |
30 | i3-input/i3-input | |
31 | i3-nagbar/i3-nagbar | |
32 | i3-msg/i3-msg | |
33 | i3-config-wizard/i3-config-wizard | |
34 | i3-dump-log/i3-dump-log | |
35 | libi3.a | |
36 | docs/*.pdf | |
37 | docs/*.html | |
38 | !/docs/refcard.html | |
39 | i3-command-parser.stamp | |
40 | i3-config-parser.stamp | |
41 | .clang_complete | |
42 | compile_commands.json | |
43 | /.ccls-cache | |
44 | /.clangd | |
45 | LAST_VERSION | |
46 | ||
47 | # We recommend building in a subdirectory called build. | |
48 | # If you chose a different directory name, | |
49 | # it is up to you to arrange for it to be ignored by git, | |
50 | # e.g. by listing your directory in .git/info/exclude. | |
51 | /build | |
52 |
0 | ||
1 | ┌──────────────────────────────┐ | |
2 | │ Release notes for i3 v4.20.1 │ | |
3 | └──────────────────────────────┘ | |
4 | ||
5 | This is i3 v4.20.1. This version is considered stable. All users of i3 are | |
6 | strongly encouraged to upgrade. | |
7 | ||
8 | ┌────────────────────────────┐ | |
9 | │ Bugfixes │ | |
10 | └────────────────────────────┘ | |
11 | ||
12 | • i3bar: fix crash with multiple monitors | |
13 | • xmlto: fix broken .TH line by extending title length | |
14 | • i3-msg: fix --raw short form (-r) in manpage | |
15 | • libi3: add missing sys/stat.h header | |
16 | • use getcwd(NULL, 0) instead of GNU extension get_current_dir_name() | |
17 | ||
18 | ┌────────────────────────────┐ | |
19 | │ Thanks! │ | |
20 | └────────────────────────────┘ | |
21 | ||
22 | Thanks for testing, bugfixes, discussions and everything I forgot go out to: | |
23 | ||
24 | rvalieris, Jakob Haufe, lycurgus, Baptiste Daroussin | |
25 | ||
26 | -- Michael Stapelberg, 2021-11-03 |
0 | ||
1 | ┌──────────────────────────────┐ | |
2 | │ Release notes for i3 v4.21 │ | |
3 | └──────────────────────────────┘ | |
4 | ||
5 | This is i3 v4.21. This version is considered stable. All users of i3 are | |
6 | strongly encouraged to upgrade. | |
7 | ||
8 | The biggest change in this release is that you can now drag tiling windows | |
9 | with your mouse (floating windows could already be dragged). For more details | |
10 | on how to use this feature, please refer to the userguide: | |
11 | ||
12 | https://i3wm.org/docs/userguide.html#_moving_tiling_containers_with_the_mouse | |
13 | ||
14 | A big thank you goes out to our core i3 developer Orestis Floros who made this | |
15 | feature possible, based on previous work from Michael Forster and Tony Crisci! | |
16 | ||
17 | ┌────────────────────────────┐ | |
18 | │ Changes in i3 v4.21 │ | |
19 | └────────────────────────────┘ | |
20 | ||
21 | • Allow dragging tiling windows with the mouse | |
22 | • Add client.focused_tab_title color option | |
23 | • Add support for multiple output names in the focus command, | |
24 | allowing users to cycle focus between e.g. VGA1 and LVDS1 but not DVI0. | |
25 | • Add a toggle option to the title_window_icon command | |
26 | • i3 switched from the obsolete PCRE 8.x regular expression matching | |
27 | library to the current PCRE2 10.x version. | |
28 | ||
29 | ┌────────────────────────────┐ | |
30 | │ Bugfixes │ | |
31 | └────────────────────────────┘ | |
32 | ||
33 | • docs/ipc: document all window_type values | |
34 | • docs/userguide: clarify the difference between the “workspace N” and | |
35 | “workspace number N” commands | |
36 | • i3bar: fix default font not being applied to bars if defined after bar block | |
37 | • i3-dmenu-desktop: add backslashes for the exec command, | |
38 | which fixes opening some .desktop files (e.g. electrum) | |
39 | • i3-sensible-pager: sanitize LESS environment variable to remove -E or -F | |
40 | • testsuite: catch i3 crashes instead of hanging on crash | |
41 | • Fix logging on machines with 256 GB of RAM | |
42 | • Do not replace existing IPC socket on start, to prevent clobbering | |
43 | the IPC socket when running i3 within i3 (e.g. in Xepyhr, for development) | |
44 | • Refuse to start without a valid IPC socket | |
45 | • Fix focus when moving container between outputs with mouse warp and | |
46 | focus_follows_mouse | |
47 | • Fix endless loop with transient_for windows | |
48 | • Fix wrong “failed” IPC reply on move workspace to output | |
49 | • Fix WM registration selection (from WM_S_S<screen> to WM_S<screen>) | |
50 | • avoid graphics artifacts when changing the layout tree by | |
51 | initializing surfaces to all black | |
52 | • update parent split con titles when child container swaps position with | |
53 | another child container | |
54 | • Fix segfault if command in bindsym is empty | |
55 | • Fix segfault with explicit mode "default" key bindings | |
56 | • Fix crash if config contains nested variables. | |
57 | • strip trailing whitespace in bar output names | |
58 | • Fix crash with long commands | |
59 | • Fix changing borders by restoring BS_NORMAL _MOTIF_WM_HINTS correctly | |
60 | ||
61 | ┌────────────────────────────┐ | |
62 | │ Thanks! │ | |
63 | └────────────────────────────┘ | |
64 | ||
65 | Thanks for testing, bugfixes, discussions and everything I forgot go out to: | |
66 | ||
67 | André Silva, Anton Älgmyr, Baptiste Daroussin, bodea, Chris Templin, George | |
68 | Rodrigues, Gergely Risko, Ingo Bürk, Jakob Haufe, Jay Ta'ala, Jeff Smith, Jonta, | |
69 | Josh Soref, Kjetil Torgrim Homme, lycurgus, mariano, Michael Forster, Orestis | |
70 | Floros, paperluigis, Peder Stray, rvalieris, sergio, Tony Crisci, takelley1, Uli | |
71 | Schlachter, viri, zhiv-git, zhrvn | |
72 | ||
73 | -- Michael Stapelberg, 2021-10-19 |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>Debugging i3: How To</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
956 | 956 | <div id="footer"> |
957 | 957 | <div id="footer-text"> |
958 | 958 | Last updated |
959 | 2021-11-03 09:23:08 CET | |
959 | 2022-09-21 18:26:43 CEST | |
960 | 960 | </div> |
961 | 961 | </div> |
962 | 962 | </body> |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>Hacking i3: How To</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
2282 | 2282 | <div id="footer"> |
2283 | 2283 | <div id="footer-text"> |
2284 | 2284 | Last updated |
2285 | 2021-11-03 09:23:08 CET | |
2285 | 2022-09-21 18:26:43 CEST | |
2286 | 2286 | </div> |
2287 | 2287 | </div> |
2288 | 2288 | </body> |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>i3bar input protocol</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
1220 | 1220 | <div id="footer"> |
1221 | 1221 | <div id="footer-text"> |
1222 | 1222 | Last updated |
1223 | 2021-11-03 09:23:08 CET | |
1223 | 2022-09-21 18:26:43 CEST | |
1224 | 1224 | </div> |
1225 | 1225 | </div> |
1226 | 1226 | </body> |
422 | 422 | following list: *title*, *instance*, *class*, *window_role*, *machine* |
423 | 423 | and *transient_for*. |
424 | 424 | window_type (string):: |
425 | The window type (_NET_WM_WINDOW_TYPE). Possible values are undefined, normal, | |
426 | dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, tooltip and | |
427 | notification. | |
425 | The window type (_NET_WM_WINDOW_TYPE). Possible values are `undefined`, | |
426 | unknown, normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | |
427 | popup_menu, tooltip and notification. | |
428 | 428 | urgent (bool):: |
429 | 429 | Whether this container (window, split container, floating container or |
430 | 430 | workspace) has the urgency hint set, directly or indirectly. All parent |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>IPC interface (interprocess communication)</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
1489 | 1489 | </dt> |
1490 | 1490 | <dd> |
1491 | 1491 | <p> |
1492 | The window type (_NET_WM_WINDOW_TYPE). Possible values are undefined, normal, | |
1493 | dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, tooltip and | |
1494 | notification. | |
1492 | The window type (_NET_WM_WINDOW_TYPE). Possible values are <code>undefined</code>, | |
1493 | unknown, normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | |
1494 | popup_menu, tooltip and notification. | |
1495 | 1495 | </p> |
1496 | 1496 | </dd> |
1497 | 1497 | <dt class="hdlist1"> |
2867 | 2867 | <div id="footer"> |
2868 | 2868 | <div id="footer-text"> |
2869 | 2869 | Last updated |
2870 | 2021-11-03 09:23:08 CET | |
2870 | 2022-09-21 18:26:43 CEST | |
2871 | 2871 | </div> |
2872 | 2872 | </div> |
2873 | 2873 | </body> |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>Layout saving in i3</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
1015 | 1015 | <div id="footer"> |
1016 | 1016 | <div id="footer-text"> |
1017 | 1017 | Last updated |
1018 | 2021-11-03 09:23:08 CET | |
1018 | 2022-09-21 18:26:43 CEST | |
1019 | 1019 | </div> |
1020 | 1020 | </div> |
1021 | 1021 | </body> |
38 | 38 | <li class='indexItem indexItem2'><a href='#get_dock_clients(%5B_%24dockarea_%5D)'>get_dock_clients([ $dockarea ])</a> |
39 | 39 | <li class='indexItem indexItem2'><a href='#cmd(%24command)'>cmd($command)</a> |
40 | 40 | <li class='indexItem indexItem2'><a href='#workspace_exists(%24workspace)'>workspace_exists($workspace)</a> |
41 | <li class='indexItem indexItem2'><a href='#focused_output'>focused_output</a> | |
41 | 42 | <li class='indexItem indexItem2'><a href='#focused_ws'>focused_ws</a> |
42 | 43 | <li class='indexItem indexItem2'><a href='#sync_with_i3(%5B_%24args_%5D)'>sync_with_i3([ $args ])</a> |
43 | 44 | <li class='indexItem indexItem2'><a href='#exit_gracefully(%24pid%2C_%5B_%24socketpath_%5D)'>exit_gracefully($pid, [ $socketpath ])</a> |
320 | 321 | |
321 | 322 | ok(workspace_exists($old_ws), 'old workspace still exists');</tt></pre> |
322 | 323 | <h2><a class='u' href='#___top' title='click to go to top of document' |
324 | name="focused_output" | |
325 | >focused_output</a></h2> | |
326 | ||
327 | <p>Returns the name of the currently focused output.</p> | |
328 | <pre><tt> is(focused_output, 'fake-0', 'i3 starts on output 0');</tt></pre> | |
329 | <h2><a class='u' href='#___top' title='click to go to top of document' | |
323 | 330 | name="focused_ws" |
324 | 331 | >focused_ws</a></h2> |
325 | 332 |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>The multi-monitor situation</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
815 | 815 | <div id="footer"> |
816 | 816 | <div id="footer-text"> |
817 | 817 | Last updated |
818 | 2021-11-03 09:23:08 CET | |
818 | 2022-09-21 18:26:43 CEST | |
819 | 819 | </div> |
820 | 820 | </div> |
821 | 821 | </body> |
3 | 3 | September 2012 |
4 | 4 | |
5 | 5 | This document explains how the i3 testsuite works, how to use it and extend it. |
6 | It is targeted at developers who not necessarily have been doing testing before | |
7 | or have not been testing in Perl before. In general, the testsuite is not of | |
6 | It is targeted at developers who haven't necessarily done testing before, | |
7 | or have not used Perl for testing before. In general, the testsuite is not of | |
8 | 8 | interest for end users. |
9 | ||
10 | 9 | |
11 | 10 | == Introduction |
12 | 11 |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>i3 testsuite</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
746 | 746 | <div id="preamble"> |
747 | 747 | <div class="sectionbody"> |
748 | 748 | <div class="paragraph"><p>This document explains how the i3 testsuite works, how to use it and extend it. |
749 | It is targeted at developers who not necessarily have been doing testing before | |
750 | or have not been testing in Perl before. In general, the testsuite is not of | |
749 | It is targeted at developers who haven’t necessarily done testing before, | |
750 | or have not used Perl for testing before. In general, the testsuite is not of | |
751 | 751 | interest for end users.</p></div> |
752 | 752 | </div> |
753 | 753 | </div> |
1468 | 1468 | <div id="footer"> |
1469 | 1469 | <div id="footer-text"> |
1470 | 1470 | Last updated |
1471 | 2021-11-03 09:23:08 CET | |
1471 | 2022-09-21 18:26:43 CEST | |
1472 | 1472 | </div> |
1473 | 1473 | </div> |
1474 | 1474 | </body> |
195 | 195 | |
196 | 196 | Floating windows are always on top of tiling windows. |
197 | 197 | |
198 | === Moving tiling containers with the mouse | |
199 | ||
200 | Since i3 4.21, it's possible to drag tiling containers using the mouse. The | |
201 | drag can be initiated either by dragging the window's titlebar or by pressing | |
202 | the <<floating_modifier>> and dragging the container while holding the | |
203 | left-click button. | |
204 | ||
205 | Once the drag is initiated and the cursor has left the original container, drop | |
206 | indicators are created according to the position of the cursor relatively to | |
207 | the target container. These indicators help you understand what the resulting | |
208 | <<tree>> layout is going to be after you release the mouse button. | |
209 | ||
210 | The possible drop positions are: | |
211 | ||
212 | Drop on container:: | |
213 | This happens when the mouse is relatively near the center of a container. | |
214 | If the mouse is released, the result is exactly as if you had run the | |
215 | +move container to mark+ command. See <<move_to_mark>>. | |
216 | Drop as sibling:: | |
217 | This happens when the mouse is relatively near the edge of a container. If | |
218 | the mouse is released, the dragged container will become a sibling of the | |
219 | target container, placed left/right/up/down according to the position of | |
220 | the indicator. | |
221 | This might or might not create a new v-split or h-split according to the | |
222 | previous layout of the target container. For example, if the target | |
223 | container is in an h-split and you drop the dragged container below it, the | |
224 | new layout will have to be a v-split. | |
225 | Drop to parent:: | |
226 | This happens when the mouse is relatively near the edge of a container (but | |
227 | even closer to the border in comparison to the sibling case above) *and* if | |
228 | that edge is also the parent container's edge. For example, if three | |
229 | containers are in a horizontal layout then edges where this can happen is | |
230 | the left edge of the left container, the right edge of the right container | |
231 | and all bottom and top edges of all three containers. | |
232 | If the mouse is released, the container is first dropped as a sibling to | |
233 | the target container, like in the case above, and then is moved | |
234 | directionally like with the +move left|right|down|up+ command. See | |
235 | <<move_direction>>. | |
236 | ||
237 | The color of the indicator matches the +client.focused+ setting. See <<client_colors>>. | |
238 | ||
239 | [[tree]] | |
198 | 240 | == Tree |
199 | 241 | |
200 | 242 | i3 stores all information about the X11 outputs, workspaces and layout of the |
537 | 579 | # The middle button over a titlebar kills the window |
538 | 580 | bindsym --release button2 kill |
539 | 581 | |
540 | # The middle button and a modifer over any part of the window kills the window | |
582 | # The middle button and a modifier over any part of the window kills the window | |
541 | 583 | bindsym --whole-window $mod+button2 kill |
542 | 584 | |
543 | 585 | # The right button toggles floating |
1040 | 1082 | workspace "2: vim" output VGA1 |
1041 | 1083 | --------------------------- |
1042 | 1084 | |
1085 | [[client_colors]] | |
1043 | 1086 | === Changing colors |
1044 | 1087 | |
1045 | 1088 | You can change all colors which i3 uses to draw the window decorations. |
1056 | 1099 | client.focused_inactive:: |
1057 | 1100 | A client which is the focused one of its container, but it does not have |
1058 | 1101 | the focus at the moment. |
1102 | client.focused_tab_title:: | |
1103 | Tab or stack container title that is the parent of the focused container | |
1104 | but not directly focused. Defaults to focused_inactive if not specified and | |
1105 | does not use the indicator and child_border colors. | |
1059 | 1106 | client.unfocused:: |
1060 | 1107 | A client which is not the focused one of its container. |
1061 | 1108 | client.urgent:: |
1099 | 1146 | programs to get information from i3, such as the current workspaces |
1100 | 1147 | (to display a workspace bar), and to control i3. |
1101 | 1148 | |
1102 | The IPC socket is enabled by default and will be created in | |
1149 | By default, an IPC socket will be created in | |
1103 | 1150 | +$XDG_RUNTIME_DIR/i3/ipc-socket.%p+ if the directory is available, falling back |
1104 | 1151 | to +/tmp/i3-%u.XXXXXX/ipc-socket.%p+, where +%u+ is your UNIX username, +%p+ is |
1105 | 1152 | the PID of i3 and XXXXXX is a string of random characters from the portable |
2207 | 2254 | focus left|right|down|up |
2208 | 2255 | focus parent|child|floating|tiling|mode_toggle |
2209 | 2256 | focus next|prev [sibling] |
2210 | focus output left|right|up|down|primary|<output> | |
2257 | focus output left|right|down|up|current|primary|next|<output1> [output2]… | |
2211 | 2258 | ---------------------------------------------- |
2212 | 2259 | |
2213 | 2260 | *Examples*: |
2227 | 2274 | # Focus last floating/tiling container |
2228 | 2275 | bindsym $mod+g focus mode_toggle |
2229 | 2276 | |
2277 | # Focus the next output (effectively toggles when you only have two outputs) | |
2278 | bindsym $mod+x move workspace to output next | |
2279 | ||
2230 | 2280 | # Focus the output right to the current one |
2231 | 2281 | bindsym $mod+x focus output right |
2232 | 2282 | |
2235 | 2285 | |
2236 | 2286 | # Focus the primary output |
2237 | 2287 | bindsym $mod+x focus output primary |
2288 | ||
2289 | # Cycle focus between outputs VGA1 and LVDS1 but not DVI0 | |
2290 | bindsym $mod+x move workspace to output VGA1 LVDS1 | |
2238 | 2291 | ------------------------------------------------- |
2239 | 2292 | |
2240 | 2293 | Note that you might not have a primary output configured yet. To do so, run: |
2242 | 2295 | xrandr --output <output> --primary |
2243 | 2296 | ------------------------- |
2244 | 2297 | |
2298 | [[move_direction]] | |
2245 | 2299 | === Moving containers |
2246 | 2300 | |
2247 | 2301 | Use the +move+ command to move a container. |
2476 | 2530 | If a workspace does not exist, the command +workspace number "1: mail"+ will |
2477 | 2531 | create workspace "1: mail". |
2478 | 2532 | |
2479 | If a workspace with number 1 does already exist, the command will switch to this | |
2533 | If a workspace with number 1 already exists, the command will switch to this | |
2480 | 2534 | workspace and ignore the text part. So even when the workspace has been renamed |
2481 | to "1: web", the above command will still switch to it. | |
2535 | "1: web", the above command will still switch to it. The command +workspace 1+ | |
2536 | will however create and move to a new workspace "1" alongside the existing | |
2537 | "1: mail" workspace. | |
2482 | 2538 | |
2483 | 2539 | === Moving workspaces to a different screen |
2484 | 2540 | |
2524 | 2580 | xrandr --output <output> --primary |
2525 | 2581 | ------------------------- |
2526 | 2582 | |
2583 | [[move_to_mark]] | |
2527 | 2584 | === Moving containers/windows to marks |
2528 | 2585 | |
2529 | 2586 | To move a container to another container with a specific mark (see <<vim_like_marks>>), |
2717 | 2774 | |
2718 | 2775 | *Syntax*: |
2719 | 2776 | ----------------------------- |
2720 | title_window_icon <yes|no> | |
2721 | title_window_icon padding <px> | |
2777 | title_window_icon <yes|no|toggle> | |
2778 | title_window_icon <padding|toggle> <px> | |
2722 | 2779 | ------------------------------ |
2723 | 2780 | |
2724 | 2781 | *Examples*: |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>i3 User’s Guide</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
969 | 969 | provided by the i3 <a href="https://github.com/i3/i3/blob/next/etc/config.keycodes">default config</a>.</p></div> |
970 | 970 | <div class="paragraph"><p>Floating windows are always on top of tiling windows.</p></div> |
971 | 971 | </div> |
972 | <div class="sect2"> | |
973 | <h3 id="_moving_tiling_containers_with_the_mouse">2.12. Moving tiling containers with the mouse</h3> | |
974 | <div class="paragraph"><p>Since i3 4.21, it’s possible to drag tiling containers using the mouse. The | |
975 | drag can be initiated either by dragging the window’s titlebar or by pressing | |
976 | the <a href="#floating_modifier">[floating_modifier]</a> and dragging the container while holding the | |
977 | left-click button.</p></div> | |
978 | <div class="paragraph"><p>Once the drag is initiated and the cursor has left the original container, drop | |
979 | indicators are created according to the position of the cursor relatively to | |
980 | the target container. These indicators help you understand what the resulting | |
981 | <a href="#tree">[tree]</a> layout is going to be after you release the mouse button.</p></div> | |
982 | <div class="paragraph"><p>The possible drop positions are:</p></div> | |
983 | <div class="dlist"><dl> | |
984 | <dt class="hdlist1"> | |
985 | Drop on container | |
986 | </dt> | |
987 | <dd> | |
988 | <p> | |
989 | This happens when the mouse is relatively near the center of a container. | |
990 | If the mouse is released, the result is exactly as if you had run the | |
991 | <code>move container to mark</code> command. See <a href="#move_to_mark">[move_to_mark]</a>. | |
992 | </p> | |
993 | </dd> | |
994 | <dt class="hdlist1"> | |
995 | Drop as sibling | |
996 | </dt> | |
997 | <dd> | |
998 | <p> | |
999 | This happens when the mouse is relatively near the edge of a container. If | |
1000 | the mouse is released, the dragged container will become a sibling of the | |
1001 | target container, placed left/right/up/down according to the position of | |
1002 | the indicator. | |
1003 | This might or might not create a new v-split or h-split according to the | |
1004 | previous layout of the target container. For example, if the target | |
1005 | container is in an h-split and you drop the dragged container below it, the | |
1006 | new layout will have to be a v-split. | |
1007 | </p> | |
1008 | </dd> | |
1009 | <dt class="hdlist1"> | |
1010 | Drop to parent | |
1011 | </dt> | |
1012 | <dd> | |
1013 | <p> | |
1014 | This happens when the mouse is relatively near the edge of a container (but | |
1015 | even closer to the border in comparison to the sibling case above) <strong>and</strong> if | |
1016 | that edge is also the parent container’s edge. For example, if three | |
1017 | containers are in a horizontal layout then edges where this can happen is | |
1018 | the left edge of the left container, the right edge of the right container | |
1019 | and all bottom and top edges of all three containers. | |
1020 | If the mouse is released, the container is first dropped as a sibling to | |
1021 | the target container, like in the case above, and then is moved | |
1022 | directionally like with the <code>move left|right|down|up</code> command. See | |
1023 | <a href="#move_direction">[move_direction]</a>. | |
1024 | </p> | |
1025 | </dd> | |
1026 | </dl></div> | |
1027 | <div class="paragraph"><p>The color of the indicator matches the <code>client.focused</code> setting. See <a href="#client_colors">[client_colors]</a>.</p></div> | |
1028 | </div> | |
972 | 1029 | </div> |
973 | 1030 | </div> |
974 | 1031 | <div class="sect1"> |
975 | <h2 id="_tree">3. Tree</h2> | |
1032 | <h2 id="tree">3. Tree</h2> | |
976 | 1033 | <div class="sectionbody"> |
977 | 1034 | <div class="paragraph"><p>i3 stores all information about the X11 outputs, workspaces and layout of the |
978 | 1035 | windows on them in a tree. The root node is the X11 root window, followed by |
1348 | 1405 | <pre><code># The middle button over a titlebar kills the window |
1349 | 1406 | bindsym --release button2 kill |
1350 | 1407 | |
1351 | # The middle button and a modifer over any part of the window kills the window | |
1408 | # The middle button and a modifier over any part of the window kills the window | |
1352 | 1409 | bindsym --whole-window $mod+button2 kill |
1353 | 1410 | |
1354 | 1411 | # The right button toggles floating |
1811 | 1868 | </div></div> |
1812 | 1869 | </div> |
1813 | 1870 | <div class="sect2"> |
1814 | <h3 id="_changing_colors">4.21. Changing colors</h3> | |
1871 | <h3 id="client_colors">4.21. Changing colors</h3> | |
1815 | 1872 | <div class="paragraph"><p>You can change all colors which i3 uses to draw the window decorations.</p></div> |
1816 | 1873 | <div class="paragraph"><p><strong>Syntax</strong>:</p></div> |
1817 | 1874 | <div class="listingblock"> |
1835 | 1892 | <p> |
1836 | 1893 | A client which is the focused one of its container, but it does not have |
1837 | 1894 | the focus at the moment. |
1895 | </p> | |
1896 | </dd> | |
1897 | <dt class="hdlist1"> | |
1898 | client.focused_tab_title | |
1899 | </dt> | |
1900 | <dd> | |
1901 | <p> | |
1902 | Tab or stack container title that is the parent of the focused container | |
1903 | but not directly focused. Defaults to focused_inactive if not specified and | |
1904 | does not use the indicator and child_border colors. | |
1838 | 1905 | </p> |
1839 | 1906 | </dd> |
1840 | 1907 | <dt class="hdlist1"> |
1901 | 1968 | <div class="paragraph"><p>i3 uses Unix sockets to provide an IPC interface. This allows third-party |
1902 | 1969 | programs to get information from i3, such as the current workspaces |
1903 | 1970 | (to display a workspace bar), and to control i3.</p></div> |
1904 | <div class="paragraph"><p>The IPC socket is enabled by default and will be created in | |
1971 | <div class="paragraph"><p>By default, an IPC socket will be created in | |
1905 | 1972 | <code>$XDG_RUNTIME_DIR/i3/ipc-socket.%p</code> if the directory is available, falling back |
1906 | 1973 | to <code>/tmp/i3-%u.XXXXXX/ipc-socket.%p</code>, where <code>%u</code> is your UNIX username, <code>%p</code> is |
1907 | 1974 | the PID of i3 and XXXXXX is a string of random characters from the portable |
3265 | 3332 | focus left|right|down|up |
3266 | 3333 | focus parent|child|floating|tiling|mode_toggle |
3267 | 3334 | focus next|prev [sibling] |
3268 | focus output left|right|up|down|primary|<output></code></pre> | |
3335 | focus output left|right|down|up|current|primary|next|<output1> [output2]…</code></pre> | |
3269 | 3336 | </div></div> |
3270 | 3337 | <div class="paragraph"><p><strong>Examples</strong>:</p></div> |
3271 | 3338 | <div class="listingblock"> |
3285 | 3352 | # Focus last floating/tiling container |
3286 | 3353 | bindsym $mod+g focus mode_toggle |
3287 | 3354 | |
3355 | # Focus the next output (effectively toggles when you only have two outputs) | |
3356 | bindsym $mod+x move workspace to output next | |
3357 | ||
3288 | 3358 | # Focus the output right to the current one |
3289 | 3359 | bindsym $mod+x focus output right |
3290 | 3360 | |
3292 | 3362 | bindsym $mod+x focus output HDMI-2 |
3293 | 3363 | |
3294 | 3364 | # Focus the primary output |
3295 | bindsym $mod+x focus output primary</code></pre> | |
3365 | bindsym $mod+x focus output primary | |
3366 | ||
3367 | # Cycle focus between outputs VGA1 and LVDS1 but not DVI0 | |
3368 | bindsym $mod+x move workspace to output VGA1 LVDS1</code></pre> | |
3296 | 3369 | </div></div> |
3297 | 3370 | <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div> |
3298 | 3371 | <div class="listingblock"> |
3301 | 3374 | </div></div> |
3302 | 3375 | </div> |
3303 | 3376 | <div class="sect2"> |
3304 | <h3 id="_moving_containers">6.5. Moving containers</h3> | |
3377 | <h3 id="move_direction">6.5. Moving containers</h3> | |
3305 | 3378 | <div class="paragraph"><p>Use the <code>move</code> command to move a container.</p></div> |
3306 | 3379 | <div class="paragraph"><p><strong>Syntax</strong>:</p></div> |
3307 | 3380 | <div class="listingblock"> |
3540 | 3613 | </div></div> |
3541 | 3614 | <div class="paragraph"><p>If a workspace does not exist, the command <code>workspace number "1: mail"</code> will |
3542 | 3615 | create workspace "1: mail".</p></div> |
3543 | <div class="paragraph"><p>If a workspace with number 1 does already exist, the command will switch to this | |
3616 | <div class="paragraph"><p>If a workspace with number 1 already exists, the command will switch to this | |
3544 | 3617 | workspace and ignore the text part. So even when the workspace has been renamed |
3545 | to "1: web", the above command will still switch to it.</p></div> | |
3618 | "1: web", the above command will still switch to it. The command <code>workspace 1</code> | |
3619 | will however create and move to a new workspace "1" alongside the existing | |
3620 | "1: mail" workspace.</p></div> | |
3546 | 3621 | </div> |
3547 | 3622 | </div> |
3548 | 3623 | <div class="sect2"> |
3588 | 3663 | </div></div> |
3589 | 3664 | </div> |
3590 | 3665 | <div class="sect2"> |
3591 | <h3 id="_moving_containers_windows_to_marks">6.11. Moving containers/windows to marks</h3> | |
3666 | <h3 id="move_to_mark">6.11. Moving containers/windows to marks</h3> | |
3592 | 3667 | <div class="paragraph"><p>To move a container to another container with a specific mark (see <a href="#vim_like_marks">[vim_like_marks]</a>), |
3593 | 3668 | you can use the following command.</p></div> |
3594 | 3669 | <div class="paragraph"><p>The window will be moved right after the marked container in the tree, i.e., it ends up |
3789 | 3864 | <div class="paragraph"><p><strong>Syntax</strong>:</p></div> |
3790 | 3865 | <div class="listingblock"> |
3791 | 3866 | <div class="content"> |
3792 | <pre><code>title_window_icon <yes|no> | |
3793 | title_window_icon padding <px></code></pre> | |
3867 | <pre><code>title_window_icon <yes|no|toggle> | |
3868 | title_window_icon <padding|toggle> <px></code></pre> | |
3794 | 3869 | </div></div> |
3795 | 3870 | <div class="paragraph"><p><strong>Examples</strong>:</p></div> |
3796 | 3871 | <div class="listingblock"> |
4195 | 4270 | <div id="footer"> |
4196 | 4271 | <div id="footer-text"> |
4197 | 4272 | Last updated |
4198 | 2021-11-03 09:23:08 CET | |
4273 | 2022-09-21 18:26:43 CEST | |
4199 | 4274 | </div> |
4200 | 4275 | </div> |
4201 | 4276 | </body> |
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
4 | 4 | <head> |
5 | 5 | <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> |
6 | <meta name="generator" content="AsciiDoc 9.1.0" /> | |
6 | <meta name="generator" content="AsciiDoc 10.2.0" /> | |
7 | 7 | <title>External workspace bars</title> |
8 | 8 | <style type="text/css"> |
9 | 9 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
843 | 843 | <div id="footer"> |
844 | 844 | <div id="footer-text"> |
845 | 845 | Last updated |
846 | 2021-11-03 09:23:08 CET | |
846 | 2022-09-21 18:26:43 CEST | |
847 | 847 | </div> |
848 | 848 | </div> |
849 | 849 | </body> |
227 | 227 | ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/); |
228 | 228 | $next_state = '__CALL'; |
229 | 229 | } |
230 | my $identifier = $token->{identifier}; | |
231 | say $tokfh qq| { "$token_name", "$identifier", $next_state, { $call_identifier } },|; | |
230 | my $identifier; | |
231 | # Set $identifier to NULL if there is no identifier | |
232 | if ($token->{identifier} eq ""){ | |
233 | $identifier = "NULL" | |
234 | } | |
235 | else{ | |
236 | $identifier = qq|"$token->{identifier}"|; | |
237 | } | |
238 | say $tokfh qq| { "$token_name", $identifier, $next_state, { $call_identifier } },|; | |
232 | 239 | } |
233 | 240 | say $tokfh '};'; |
234 | 241 | } |
330 | 330 | * separate configuration directives. */ |
331 | 331 | while ((*walk == ' ' || *walk == '\t') && *walk != '\0') |
332 | 332 | walk++; |
333 | ||
334 | //printf("remaining input: %s\n", walk); | |
335 | 333 | |
336 | 334 | cmdp_token_ptr *ptr = &(tokens[state]); |
337 | 335 | for (c = 0; c < ptr->n; c++) { |
425 | 423 | } |
426 | 424 | |
427 | 425 | if (strcmp(token->name, "end") == 0) { |
428 | //printf("checking for end: *%s*\n", walk); | |
429 | 426 | if (*walk == '\0' || *walk == '\n' || *walk == '\r') { |
430 | 427 | if ((result = next_state(token)) != NULL) |
431 | 428 | return result; |
432 | /* To make sure we start with an appropriate matching | |
433 | * datastructure for commands which do *not* specify any | |
429 | /* To make sure we start with an appropriate matching data | |
430 | * structure for commands which do *not* specify any | |
434 | 431 | * criteria, we re-initialize the criteria system after |
435 | 432 | * every command. */ |
436 | // TODO: make this testable | |
437 | 433 | walk++; |
438 | 434 | break; |
439 | 435 | } |
481 | 481 | # double quote which is NOT preceded by a backslash (\). |
482 | 482 | # |
483 | 483 | # Therefore, we escape all double quotes (") by replacing them with \" |
484 | $exec =~ s/"/\\"/g; | |
484 | $exec =~ s/\\"/\\\\\\"/g; | |
485 | $exec =~ s/([^\\])"/$1\\"/g; | |
485 | 486 | |
486 | 487 | if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) { |
487 | 488 | $nosn = '--no-startup-id'; |
0 | 0 | # This list can be used to convert X11 Keysyms to Unicode 2.1 character. |
1 | 1 | # The list is not checked for correctness by Unicode officials. Use it |
2 | # at your own risk and the creator is not responsable for any damage that | |
2 | # at your own risk and the creator is not responsible for any damage that | |
3 | 3 | # occurred due to using this list. |
4 | 4 | # |
5 | 5 | # The list is created by looking at the Keysym names and the Unicode data |
7 | 7 | # Distributions/packagers can enhance this script with a |
8 | 8 | # distribution-specific mechanism to find the preferred pager. |
9 | 9 | |
10 | # The less -E and -F options exit immediately for short files, strip if present. | |
11 | case "$LESS" in | |
12 | *[EF]*) LESS=`echo "$LESS" | tr -d EF` | |
13 | esac | |
14 | ||
10 | 15 | # Hopefully one of these is installed (no flamewars about preference please!): |
11 | 16 | # We don't use 'more' because it will exit if the file is too short. |
12 | 17 | # Worst case scenario we'll open the file in your editor. |
13 | 13 | # 2. Distribution-specific mechanisms come next, e.g. x-terminal-emulator |
14 | 14 | # 3. The terminal emulator with best accessibility comes first. |
15 | 15 | # 4. No order is guaranteed/desired for the remaining terminal emulators. |
16 | for terminal in "$TERMINAL" x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper; do | |
16 | for terminal in "$TERMINAL" x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper wezterm; do | |
17 | 17 | if command -v "$terminal" > /dev/null 2>&1; then |
18 | 18 | exec "$terminal" "$@" |
19 | 19 | fi |
65 | 65 | uint32_t border_left; |
66 | 66 | bool pango_markup; |
67 | 67 | |
68 | /* The amount of pixels necessary to render a separater after the block. */ | |
68 | /* The amount of pixels necessary to render a separator after the block. */ | |
69 | 69 | uint32_t sep_block_width; |
70 | 70 | |
71 | 71 | /* Continuously-updated information on how to render this status block. */ |
12 | 12 | #include <stdlib.h> |
13 | 13 | #include <string.h> |
14 | 14 | |
15 | #include <X11/Xlib.h> | |
16 | 15 | #include <yajl/yajl_parse.h> |
17 | 16 | |
18 | 17 | config_t config; |
125 | 124 | } |
126 | 125 | |
127 | 126 | if (len == strlen("shift") && !strncmp((const char *)val, "shift", strlen("shift"))) { |
128 | config.modifier = ShiftMask; | |
127 | config.modifier = XCB_MOD_MASK_SHIFT; | |
129 | 128 | return 1; |
130 | 129 | } |
131 | 130 | if (len == strlen("ctrl") && !strncmp((const char *)val, "ctrl", strlen("ctrl"))) { |
132 | config.modifier = ControlMask; | |
131 | config.modifier = XCB_MOD_MASK_CONTROL; | |
133 | 132 | return 1; |
134 | 133 | } |
135 | 134 | if (len == strlen("Mod") + 1 && !strncmp((const char *)val, "Mod", strlen("Mod"))) { |
136 | 135 | switch (val[3]) { |
137 | 136 | case '1': |
138 | config.modifier = Mod1Mask; | |
137 | config.modifier = XCB_MOD_MASK_1; | |
139 | 138 | return 1; |
140 | 139 | case '2': |
141 | config.modifier = Mod2Mask; | |
140 | config.modifier = XCB_MOD_MASK_2; | |
142 | 141 | return 1; |
143 | 142 | case '3': |
144 | config.modifier = Mod3Mask; | |
143 | config.modifier = XCB_MOD_MASK_3; | |
145 | 144 | return 1; |
146 | 145 | case '5': |
147 | config.modifier = Mod5Mask; | |
146 | config.modifier = XCB_MOD_MASK_5; | |
148 | 147 | return 1; |
149 | 148 | } |
150 | 149 | } |
151 | 150 | |
152 | config.modifier = Mod4Mask; | |
151 | config.modifier = XCB_MOD_MASK_4; | |
153 | 152 | return 1; |
154 | 153 | } |
155 | 154 |
202 | 202 | if (block->border) |
203 | 203 | render->width += logical_px(block->border_left + block->border_right); |
204 | 204 | |
205 | /* Compute offset and append for text aligment in min_width. */ | |
205 | /* Compute offset and append for text alignment in min_width. */ | |
206 | 206 | if (block->min_width <= render->width) { |
207 | 207 | render->x_offset = 0; |
208 | 208 | render->x_append = 0; |
57 | 57 | #include "match.h" |
58 | 58 | #include "xcursor.h" |
59 | 59 | #include "resize.h" |
60 | #include "tiling_drag.h" | |
60 | 61 | #include "sighandler.h" |
61 | 62 | #include "move.h" |
62 | 63 | #include "output.h" |
332 | 332 | void cmd_debuglog(I3_CMD, const char *argument); |
333 | 333 | |
334 | 334 | /** |
335 | * Implementation of 'title_window_icon <yes|no>' and 'title_window_icon padding <px>' | |
335 | * Implementation of 'title_window_icon <yes|no|toggle>' and 'title_window_icon <padding|toggle> <px>' | |
336 | 336 | * |
337 | 337 | */ |
338 | 338 | void cmd_title_window_icon(I3_CMD, const char *enable, int padding); |
13 | 13 | #include <yajl/yajl_gen.h> |
14 | 14 | |
15 | 15 | /** |
16 | * Holds an intermediate represenation of the result of a call to any command. | |
16 | * Holds an intermediate representation of the result of a call to any command. | |
17 | 17 | * When calling parse_command("floating enable, border none"), the parser will |
18 | 18 | * internally use this struct when calling cmd_floating and cmd_border. |
19 | 19 | */ |
206 | 206 | * |
207 | 207 | */ |
208 | 208 | Con *con_by_mark(const char *mark); |
209 | ||
210 | /** | |
211 | * Start from a container and traverse the transient_for linked list. Returns | |
212 | * true if target window is found in the list. Protects againsts potential | |
213 | * cycles. | |
214 | * | |
215 | */ | |
216 | bool con_find_transient_for_window(Con *start, xcb_window_t target); | |
209 | 217 | |
210 | 218 | /** |
211 | 219 | * Returns true if and only if the given containers holds the mark. |
362 | 370 | */ |
363 | 371 | bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates); |
364 | 372 | |
373 | bool con_move_to_target(Con *con, Con *target); | |
365 | 374 | /** |
366 | 375 | * Moves the given container to the given mark. |
367 | 376 | * |
40 | 40 | Match current_match; |
41 | 41 | |
42 | 42 | /* A list which contains the states that lead to the current state, e.g. |
43 | * INITIAL, WORKSPACE_LAYOUT. | |
44 | * When jumping back to INITIAL, statelist_idx will simply be set to 1 | |
45 | * (likewise for other states, e.g. MODE or BAR). | |
46 | * This list is used to process the nearest error token. */ | |
43 | * INITIAL, WORKSPACE_LAYOUT. | |
44 | * When jumping back to INITIAL, statelist_idx will simply be set to 1 | |
45 | * (likewise for other states, e.g. MODE or BAR). | |
46 | * This list is used to process the nearest error token. */ | |
47 | 47 | int statelist[10]; |
48 | 48 | /* NB: statelist_idx points to where the next entry will be inserted */ |
49 | 49 | int statelist_idx; |
50 | 50 | |
51 | 51 | /******************************************************************************* |
52 | * The (small) stack where identified literals are stored during the parsing | |
53 | * of a single config directive (like $workspace). | |
54 | ******************************************************************************/ | |
52 | * The (small) stack where identified literals are stored during the parsing | |
53 | * of a single config directive (like $workspace). | |
54 | ******************************************************************************/ | |
55 | 55 | struct stack *stack; |
56 | 56 | |
57 | 57 | struct variables_head variables; |
60 | 60 | }; |
61 | 61 | |
62 | 62 | /** |
63 | * An intermediate reprsentation of the result of a parse_config call. | |
63 | * An intermediate representation of the result of a parse_config call. | |
64 | 64 | * Currently unused, but the JSON output will be useful in the future when we |
65 | 65 | * implement a config parsing IPC command. |
66 | 66 | * |
237 | 237 | color_t background; |
238 | 238 | struct Colortriple focused; |
239 | 239 | struct Colortriple focused_inactive; |
240 | struct Colortriple focused_tab_title; | |
240 | 241 | struct Colortriple unfocused; |
241 | 242 | struct Colortriple urgent; |
242 | 243 | struct Colortriple placeholder; |
244 | bool got_focused_tab_title; | |
243 | 245 | } client; |
244 | 246 | struct config_bar { |
245 | 247 | struct Colortriple focused; |
8 | 8 | */ |
9 | 9 | #pragma once |
10 | 10 | |
11 | #define PCRE2_CODE_UNIT_WIDTH 8 | |
12 | ||
11 | 13 | #define SN_API_NOT_YET_FROZEN 1 |
12 | 14 | #include <libsn/sn-launcher.h> |
13 | 15 | |
14 | 16 | #include <xcb/randr.h> |
15 | #include <pcre.h> | |
17 | #include <pcre2.h> | |
16 | 18 | #include <sys/time.h> |
17 | 19 | #include <cairo/cairo.h> |
18 | 20 | |
247 | 249 | */ |
248 | 250 | struct regex { |
249 | 251 | char *pattern; |
250 | pcre *regex; | |
251 | pcre_extra *extra; | |
252 | pcre2_code *regex; | |
252 | 253 | }; |
253 | 254 | |
254 | 255 | /** |
661 | 662 | char *title_format; |
662 | 663 | |
663 | 664 | /** Whether the window icon should be displayed, and with what padding. -1 |
664 | * means display no window icon (default behavior), 0 means display without | |
665 | * any padding, 1 means display with 1 pixel of padding and so on. */ | |
665 | * means display no window icon (default behavior), 0 means display without | |
666 | * any padding, 1 means display with 1 pixel of padding and so on. */ | |
666 | 667 | int window_icon_padding; |
667 | 668 | |
668 | 669 | /* a sticky-group is an identifier which bundles several containers to a |
13 | 13 | /** |
14 | 14 | * Connects to i3 to find out the currently running version. Useful since it |
15 | 15 | * might be different from the version compiled into this binary (maybe the |
16 | * user didn’t correctly install i3 or forgot te restart it). | |
16 | * user didn’t correctly install i3 or forgot to restart it). | |
17 | 17 | * |
18 | 18 | * The output looks like this: |
19 | 19 | * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) |
55 | 55 | extern SnDisplay *sndisplay; |
56 | 56 | extern xcb_key_symbols_t *keysyms; |
57 | 57 | extern char **start_argv; |
58 | extern Display *xlibdpy, *xkbdpy; | |
59 | 58 | extern int xkb_current_group; |
60 | 59 | extern TAILQ_HEAD(bindings_head, Binding) *bindings; |
61 | 60 | extern const char *current_binding_mode; |
294 | 294 | int ipc_connect(const char *socket_path); |
295 | 295 | |
296 | 296 | /** |
297 | * Connects to the socket at the given path with no fallback paths. Returns | |
298 | * -1 if connect() fails and die()s for other errors. | |
299 | */ | |
300 | int ipc_connect_impl(const char *socket_path); | |
301 | ||
302 | /** | |
297 | 303 | * Formats a message (payload) of the given size and type and sends it to i3 via |
298 | 304 | * the given socket file descriptor. |
299 | 305 | * |
48 | 48 | * |
49 | 49 | */ |
50 | 50 | void init_ws_for_output(Output *output); |
51 | ||
52 | /** | |
53 | * Initializes the specified output, assigning the specified workspace to it. | |
54 | * | |
55 | */ | |
56 | //void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace); | |
57 | 51 | |
58 | 52 | /** |
59 | 53 | * (Re-)queries the outputs via RandR and stores them in the list of outputs. |
0 | /* | |
1 | * vim:ts=4:sw=4:expandtab | |
2 | * | |
3 | * i3 - an improved dynamic tiling window manager | |
4 | * © 2009 Michael Stapelberg and contributors (see also: LICENSE) | |
5 | * | |
6 | * tiling_drag.h: Reposition tiled windows by dragging. | |
7 | * | |
8 | */ | |
9 | #pragma once | |
10 | ||
11 | /** | |
12 | * Initiates a mouse drag operation on a tiled window. | |
13 | * | |
14 | */ | |
15 | void tiling_drag(Con *con, xcb_button_press_event_t *event); |
182 | 182 | * |
183 | 183 | */ |
184 | 184 | direction_t direction_from_orientation_position(orientation_t orientation, position_t position); |
185 | ||
186 | /** | |
187 | * Converts direction to a string representation. | |
188 | * | |
189 | */ | |
190 | const char *direction_to_string(direction_t direction); | |
191 | ||
192 | /** | |
193 | * Converts position to a string representation. | |
194 | * | |
195 | */ | |
196 | const char *position_to_string(position_t position); |
93 | 93 | * it is still in use by popular widget toolkits such as GTK+ and Java AWT. |
94 | 94 | * |
95 | 95 | */ |
96 | void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style); | |
96 | bool window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style); | |
97 | 97 | |
98 | 98 | /** |
99 | 99 | * Updates the WM_CLIENT_MACHINE |
91 | 91 | |
92 | 92 | /** |
93 | 93 | * Returns true if the workspace is currently visible. Especially important for |
94 | * multi-monitor environments, as they can have multiple currenlty active | |
94 | * multi-monitor environments, as they can have multiple currently active | |
95 | 95 | * workspaces. |
96 | 96 | * |
97 | 97 | */ |
9 | 9 | #include <unistd.h> |
10 | 10 | #include <libgen.h> |
11 | 11 | #include <err.h> |
12 | #include <errno.h> | |
12 | 13 | #include <fcntl.h> |
13 | 14 | #include <stdlib.h> |
14 | 15 | #include <string.h> |
33 | 34 | } |
34 | 35 | free(copy); |
35 | 36 | |
37 | /* Check if the socket is in use by another process (this call does not | |
38 | * succeed if the socket is stale / the owner already exited) */ | |
39 | int sockfd = ipc_connect_impl(resolved); | |
40 | if (sockfd != -1) { | |
41 | ELOG("Refusing to create UNIX socket at %s: Socket is already in use\n", resolved); | |
42 | close(sockfd); | |
43 | errno = EEXIST; | |
44 | return -1; | |
45 | } | |
46 | ||
36 | 47 | /* Unlink the unix domain socket before */ |
37 | 48 | unlink(resolved); |
38 | 49 | |
39 | int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); | |
50 | sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); | |
40 | 51 | if (sockfd < 0) { |
41 | 52 | perror("socket()"); |
42 | 53 | free(resolved); |
159 | 159 | font.type = FONT_TYPE_NONE; |
160 | 160 | font.pattern = NULL; |
161 | 161 | |
162 | /* No XCB connction, return early because we're just validating the | |
162 | /* No XCB connection, return early because we're just validating the | |
163 | 163 | * configuration file. */ |
164 | 164 | if (conn == NULL) { |
165 | 165 | return font; |
12 | 12 | #include <string.h> |
13 | 13 | #include <sys/socket.h> |
14 | 14 | #include <sys/un.h> |
15 | #include <unistd.h> | |
15 | 16 | |
16 | 17 | /* |
17 | 18 | * Connects to the i3 IPC socket and returns the file descriptor for the |
38 | 39 | path = sstrdup("/tmp/i3-ipc.sock"); |
39 | 40 | } |
40 | 41 | |
42 | int sockfd = ipc_connect_impl(path); | |
43 | if (sockfd < 0) { | |
44 | err(EXIT_FAILURE, "Could not connect to i3 on socket %s", path); | |
45 | } | |
46 | free(path); | |
47 | return sockfd; | |
48 | } | |
49 | ||
50 | /** | |
51 | * Connects to the socket at the given path with no fallback paths. Returns | |
52 | * -1 if connect() fails and die()s for other errors. | |
53 | * | |
54 | */ | |
55 | int ipc_connect_impl(const char *socket_path) { | |
41 | 56 | int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); |
42 | 57 | if (sockfd == -1) |
43 | 58 | err(EXIT_FAILURE, "Could not create socket"); |
47 | 62 | struct sockaddr_un addr; |
48 | 63 | memset(&addr, 0, sizeof(struct sockaddr_un)); |
49 | 64 | addr.sun_family = AF_LOCAL; |
50 | strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); | |
51 | if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) | |
52 | err(EXIT_FAILURE, "Could not connect to i3 on socket %s", path); | |
53 | free(path); | |
65 | strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); | |
66 | if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { | |
67 | close(sockfd); | |
68 | return -1; | |
69 | } | |
54 | 70 | return sockfd; |
55 | 71 | } |
1 | 1 | .\" Title: i3-config-wizard |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-CONFIG\-WIZARD" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-CONFIG\-WIZARD" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
0 | .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) | |
0 | .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) | |
1 | 1 | .\" |
2 | 2 | .\" Standard preamble: |
3 | 3 | .\" ======================================================================== |
70 | 70 | .\" ======================================================================== |
71 | 71 | .\" |
72 | 72 | .IX Title "I3-DMENU-DESKTOP 1" |
73 | .TH I3-DMENU-DESKTOP 1 "2021-11-03" "perl v5.34.0" "User Contributed Perl Documentation" | |
73 | .TH I3-DMENU-DESKTOP 1 "2022-09-21" "perl v5.36.0" "User Contributed Perl Documentation" | |
74 | 74 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes |
75 | 75 | .\" way too many mistakes in technical documents. |
76 | 76 | .if n .ad l |
1 | 1 | .\" Title: i3-dump-log |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-DUMP\-LOG" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-DUMP\-LOG" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3-input |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-INPUT" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-INPUT" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
62 | 62 | .RS 4 |
63 | 63 | Use the specified X11 core font (use |
64 | 64 | xfontsel |
65 | to chose a font)\&. | |
65 | to choose a font)\&. | |
66 | 66 | .RE |
67 | 67 | .PP |
68 | 68 | \-v |
38 | 38 | The prompt string is not included in the user input/command. |
39 | 39 | |
40 | 40 | -f <font>:: |
41 | Use the specified X11 core font (use +xfontsel+ to chose a font). | |
41 | Use the specified X11 core font (use +xfontsel+ to choose a font). | |
42 | 42 | |
43 | 43 | -v:: |
44 | 44 | Show version and exit. |
1 | 1 | .\" Title: i3-migrate-config-to-v4 |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-MIGRATE\-CONFIG\-TO\-V4" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-MIGRATE\-CONFIG\-TO\-V4" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3-msg |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-MSG" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-MSG" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3-nagbar |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-NAGBAR" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-NAGBAR" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
0 | .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) | |
0 | .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) | |
1 | 1 | .\" |
2 | 2 | .\" Standard preamble: |
3 | 3 | .\" ======================================================================== |
70 | 70 | .\" ======================================================================== |
71 | 71 | .\" |
72 | 72 | .IX Title "I3-SAVE-TREE 1" |
73 | .TH I3-SAVE-TREE 1 "2021-11-03" "perl v5.34.0" "User Contributed Perl Documentation" | |
73 | .TH I3-SAVE-TREE 1 "2022-09-21" "perl v5.36.0" "User Contributed Perl Documentation" | |
74 | 74 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes |
75 | 75 | .\" way too many mistakes in technical documents. |
76 | 76 | .if n .ad l |
1 | 1 | .\" Title: i3-sensible-editor |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-SENSIBLE\-EDITOR" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-SENSIBLE\-EDITOR" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3-sensible-pager |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-SENSIBLE\-PAGER" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-SENSIBLE\-PAGER" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3-sensible-terminal |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3\-SENSIBLE\-TERMINAL" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3\-SENSIBLE\-TERMINAL" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3 |
2 | 2 | .\" Author: [see the "AUTHOR" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
1 | 1 | .\" Title: i3bar |
2 | 2 | .\" Author: [see the "AUTHORS" section] |
3 | 3 | .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> |
4 | .\" Date: 11/03/2021 | |
4 | .\" Date: 09/21/2022 | |
5 | 5 | .\" Manual: i3 Manual |
6 | .\" Source: i3 4.20.1 | |
6 | .\" Source: i3 4.21 | |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3BAR" "1" "11/03/2021" "i3 4\&.20\&.1" "i3 Manual" | |
9 | .TH "I3BAR" "1" "09/21/2022" "i3 4\&.21" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
5 | 5 | project( |
6 | 6 | 'i3', |
7 | 7 | 'c', |
8 | version: '4.20.1', | |
8 | version: '4.21', | |
9 | 9 | default_options: [ |
10 | 10 | 'c_std=c11', |
11 | 11 | 'warning_level=1', # enable all warnings (-Wall) |
62 | 62 | sources: vcs_tag( |
63 | 63 | input: config_h_in, |
64 | 64 | output: 'config.h', |
65 | fallback: meson.project_version() + ' (2021-11-03)', | |
65 | fallback: meson.project_version() + ' (2022-09-21)', | |
66 | 66 | ) |
67 | 67 | ) |
68 | 68 | |
315 | 315 | xkbcommon_dep = dependency('xkbcommon', method: 'pkg-config') |
316 | 316 | xkbcommon_x11_dep = dependency('xkbcommon-x11', method: 'pkg-config') |
317 | 317 | yajl_dep = dependency('yajl', method: 'pkg-config') |
318 | libpcre_dep = dependency('libpcre', version: '>=8.10', method: 'pkg-config') | |
318 | libpcre_dep = dependency('libpcre2-8', version: '>=10', method: 'pkg-config') | |
319 | 319 | cairo_dep = dependency('cairo', version: '>=1.14.4', method: 'pkg-config') |
320 | 320 | pangocairo_dep = dependency('pangocairo', method: 'pkg-config') |
321 | 321 | glib_dep = dependency('glib-2.0', method: 'pkg-config') |
408 | 408 | 'src/sighandler.c', |
409 | 409 | 'src/startup.c', |
410 | 410 | 'src/sync.c', |
411 | 'src/tiling_drag.c', | |
411 | 412 | 'src/tree.c', |
412 | 413 | 'src/util.c', |
413 | 414 | 'src/version.c', |
165 | 165 | -> call cmd_focus_direction($direction) |
166 | 166 | |
167 | 167 | state FOCUS_OUTPUT: |
168 | output = string | |
169 | -> call cmd_focus_output($output) | |
168 | output = word | |
169 | -> call cmd_focus_output($output); FOCUS_OUTPUT | |
170 | end | |
171 | -> call cmd_focus_output(NULL); INITIAL | |
170 | 172 | |
171 | 173 | # kill [window|client] |
172 | 174 | state KILL: |
465 | 467 | state TITLE_WINDOW_ICON: |
466 | 468 | 'padding' |
467 | 469 | -> TITLE_WINDOW_ICON_PADDING |
470 | enable = 'toggle' | |
471 | -> TITLE_WINDOW_ICON_PADDING | |
468 | 472 | enable = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive' |
469 | 473 | -> call cmd_title_window_icon($enable, 0) |
470 | 474 |
55 | 55 | exectype = 'exec_always', 'exec' -> EXEC |
56 | 56 | colorclass = 'client.background' |
57 | 57 | -> COLOR_SINGLE |
58 | colorclass = 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder' | |
58 | colorclass = 'client.focused_inactive', 'client.focused_tab_title', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder' | |
59 | 59 | -> COLOR_BORDER |
60 | 60 | |
61 | 61 | # We ignore comments and 'set' lines (variables). |
398 | 398 | exclude_titlebar = '--exclude-titlebar' |
399 | 399 | -> |
400 | 400 | command = string |
401 | -> call cfg_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $exclude_titlebar, $command) | |
402 | end | |
403 | 401 | -> call cfg_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $exclude_titlebar, $command) |
404 | 402 | |
405 | 403 | ################################################################################ |
566 | 564 | -> call cfg_bar_position($position); BAR |
567 | 565 | |
568 | 566 | state BAR_OUTPUT: |
569 | output = string | |
567 | output = word | |
570 | 568 | -> call cfg_bar_output($output); BAR |
571 | 569 | |
572 | 570 | state BAR_TRAY_OUTPUT: |
0 | fix crash with "layout default" |
0 | Do not replace existing IPC socket on start |
0 | fix focus when moving container between outputs with mouse warp and focus_follows_mouse |
0 | Fix endless loop with transient_for windows |
0 | fix wrong failed reply on move workspace to output |
0 | changed WM registration selection from WM_S_S<screen> to WM_S<screen> |
0 | avoid graphics artifacts when changing the layout tree by initializing surfaces to all black |
0 | Fix segfault if command in bindsym is empty |
0 | update parent split con titles when child con swaps position with another child con |
0 | fix default font not being applied to bars if defined after bar block |
0 | strip trailing whitespace in bar output names |
0 | Fix crash if config contains nested variables. |
0 | Fix segfault with explicit mode "default" key bindings. |
0 | Restore BS_NORMAL _MOTIF_WM_HINTS correctly |
0 | Acquire the WM_Sn selection when starting as required by ICCCM |
0 | Refuse to start without valid IPC socket |
0 | Add client.focused_tab_title color option |
0 | Add support for multiple outputs in focus command |
0 | Allow moving tiling windows with the mouse |
0 | Add title_window_icon toggle |
0 | #!/usr/bin/env perl | |
1 | use strict; | |
2 | use warnings; | |
3 | use v5.10; | |
4 | use Getopt::Long; | |
5 | ||
6 | my @template = ( | |
7 | ' | |
8 | ┌──────────────────────────────┐ | |
9 | │ Release notes for i3 v4.21 │ | |
10 | └──────────────────────────────┘ | |
11 | ||
12 | This is i3 v4.21. This version is considered stable. All users of i3 are | |
13 | strongly encouraged to upgrade. | |
14 | ||
15 | ||
16 | ┌────────────────────────────┐ | |
17 | │ Changes in i3 v4.21 │ | |
18 | └────────────────────────────┘ | |
19 | ||
20 | ', | |
21 | ' | |
22 | ┌────────────────────────────┐ | |
23 | │ Bugfixes │ | |
24 | └────────────────────────────┘ | |
25 | ||
26 | '); | |
27 | ||
28 | my $print_urls = 0; | |
29 | my $result = GetOptions('print-urls' => \$print_urls); | |
30 | ||
31 | sub get_number { | |
32 | my $s = shift; | |
33 | return $1 if $s =~ m/^(\d+)/; | |
34 | return -1; | |
35 | } | |
36 | ||
37 | sub read_changefiles { | |
38 | my $dirpath = shift; | |
39 | opendir my $dir, $dirpath or die "Cannot open directory $dirpath: $!"; | |
40 | my @files = sort { get_number($a) <=> get_number($b) } readdir $dir; | |
41 | ||
42 | closedir $dir; | |
43 | ||
44 | my $s = ''; | |
45 | for my $filename (@files) { | |
46 | next if $filename eq '.'; | |
47 | next if $filename eq '..'; | |
48 | next if $filename eq '0-example'; | |
49 | ||
50 | die "Filename $filename should start with a number (e.g. the pull request number)" unless get_number($filename) > 0; | |
51 | ||
52 | $filename = $dirpath . '/' . $filename; | |
53 | open my $in, '<', $filename or die "can't open $filename: $!"; | |
54 | my @lines = <$in>; | |
55 | close $in or die "can't close $filename: $!"; | |
56 | ||
57 | my $content = trim(join("\n ", map { trim($_) } @lines)); | |
58 | die "$filename can't be empty" unless length($content) > 0; | |
59 | ||
60 | my $url = ''; | |
61 | if ($print_urls) { | |
62 | my $commit = `git log --diff-filter=A --pretty=format:"%H" $filename`; | |
63 | $commit = trim($commit) if defined($commit); | |
64 | die "$filename: git log failed to find commit" if ($?) || (length($commit) == 0); | |
65 | ||
66 | my $pr = find_pr($commit); | |
67 | $url = 'https://github.com/i3/i3/commit/' . $commit; | |
68 | $url = 'https://github.com/i3/i3/pull/' . $pr if defined($pr); | |
69 | $url = $url . "\n"; | |
70 | } | |
71 | ||
72 | $s = $s . ' • ' . $content . "\n" . $url; | |
73 | } | |
74 | return $s; | |
75 | } | |
76 | ||
77 | sub find_pr { | |
78 | my $hash = shift; | |
79 | my $result = `git log --merges --ancestry-path --oneline $hash..next | grep 'Merge pull request' | tail -n1`; | |
80 | return unless defined($result); | |
81 | ||
82 | return unless ($result =~ /Merge pull request .([0-9]+)/); | |
83 | return $1; | |
84 | } | |
85 | ||
86 | sub trim { | |
87 | (my $s = $_[0]) =~ s/^\s+|\s+$//g; | |
88 | return $s; | |
89 | } | |
90 | ||
91 | # Expected to run for i3's git root | |
92 | my $changes = read_changefiles('release-notes/changes'); | |
93 | my $bugfixes = read_changefiles('release-notes/bugfixes'); | |
94 | ||
95 | print $template[0] . $changes . $template[1] . $bugfixes; |
79 | 79 | */ |
80 | 80 | static bool floating_mod_on_tiled_client(Con *con, xcb_button_press_event_t *event) { |
81 | 81 | /* The client is in tiling layout. We can still initiate a resize with the |
82 | * right mouse button, by chosing the border which is the most near one to | |
82 | * right mouse button, by choosing the border which is the most near one to | |
83 | 83 | * the position of the mouse pointer */ |
84 | 84 | int to_right = con->rect.width - event->event_x, |
85 | 85 | to_left = event->event_x, |
186 | 186 | if (!ws) |
187 | 187 | goto done; |
188 | 188 | } |
189 | ||
190 | if (ws != focused_workspace) | |
191 | workspace_show(ws); | |
192 | 189 | |
193 | 190 | /* get the floating con */ |
194 | 191 | Con *floatingcon = con_inside_floating(con); |
217 | 214 | goto done; |
218 | 215 | } |
219 | 216 | |
220 | /* 2: focus this con or one of its children. */ | |
217 | /* 2: floating modifier pressed, initiate a drag */ | |
218 | if (mod_pressed && event->detail == XCB_BUTTON_INDEX_1 && !floatingcon) { | |
219 | tiling_drag(con, event); | |
220 | goto done; | |
221 | } | |
222 | ||
223 | /* 3: focus this con or one of its children. */ | |
221 | 224 | Con *con_to_focus = con; |
222 | 225 | if (in_stacked && dest == CLICK_DECORATION) { |
223 | 226 | /* If the container is a tab/stacked container and the click happened |
230 | 233 | } |
231 | 234 | } |
232 | 235 | } |
236 | if (ws != focused_workspace) { | |
237 | workspace_show(ws); | |
238 | } | |
233 | 239 | con_activate(con_to_focus); |
234 | 240 | |
235 | /* 3: For floating containers, we also want to raise them on click. | |
241 | /* 4: For floating containers, we also want to raise them on click. | |
236 | 242 | * We will skip handling events on floating cons in fullscreen mode */ |
237 | 243 | Con *fs = con_get_fullscreen_covering_ws(ws); |
238 | 244 | if (floatingcon != NULL && fs != con) { |
239 | /* 4: floating_modifier plus left mouse button drags */ | |
245 | /* 5: floating_modifier plus left mouse button drags */ | |
240 | 246 | if (mod_pressed && is_left_click) { |
241 | 247 | floating_drag_window(floatingcon, event, false); |
242 | 248 | return; |
243 | 249 | } |
244 | 250 | |
245 | /* 5: resize (floating) if this was a (left or right) click on the | |
251 | /* 6: resize (floating) if this was a (left or right) click on the | |
246 | 252 | * left/right/bottom border, or a right click on the decoration. |
247 | 253 | * also try resizing (tiling) if possible */ |
248 | 254 | if (mod_pressed && is_right_click) { |
271 | 277 | return; |
272 | 278 | } |
273 | 279 | |
274 | /* 6: dragging, if this was a click on a decoration (which did not lead | |
280 | /* 7: dragging, if this was a click on a decoration (which did not lead | |
275 | 281 | * to a resize) */ |
276 | 282 | if (dest == CLICK_DECORATION && is_left_click) { |
277 | 283 | floating_drag_window(floatingcon, event, !was_focused); |
281 | 287 | goto done; |
282 | 288 | } |
283 | 289 | |
284 | /* 7: floating modifier pressed, initiate a resize */ | |
290 | /* 8: floating modifier pressed, initiate a drag */ | |
291 | if ((mod_pressed || dest == CLICK_DECORATION) && event->detail == XCB_BUTTON_INDEX_1) { | |
292 | tiling_drag(con, event); | |
293 | goto done; | |
294 | } | |
295 | ||
296 | /* 9: floating modifier pressed, initiate a resize */ | |
285 | 297 | if (dest == CLICK_INSIDE && mod_pressed && is_right_click) { |
286 | 298 | if (floating_mod_on_tiled_client(con, event)) { |
287 | 299 | return; |
292 | 304 | xcb_flush(conn); |
293 | 305 | return; |
294 | 306 | } |
295 | /* 8: otherwise, check for border/decoration clicks and resize */ | |
307 | /* 10: otherwise, check for border/decoration clicks and resize */ | |
296 | 308 | if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) && |
297 | 309 | is_left_or_right_click) { |
298 | 310 | DLOG("Trying to resize (tiling)\n"); |
1022 | 1022 | ysuccess(true); |
1023 | 1023 | } |
1024 | 1024 | |
1025 | typedef struct user_output_name { | |
1026 | char *name; | |
1027 | TAILQ_ENTRY(user_output_name) user_output_names; | |
1028 | } user_output_name; | |
1029 | typedef TAILQ_HEAD(user_output_names_head, user_output_name) user_output_names_head; | |
1030 | ||
1031 | static void user_output_names_add(user_output_names_head *list, const char *name) { | |
1032 | if (strcmp(name, "next") == 0) { | |
1033 | /* "next" here works like a wildcard: It "expands" to all available | |
1034 | * outputs. */ | |
1035 | Output *output; | |
1036 | TAILQ_FOREACH (output, &outputs, outputs) { | |
1037 | user_output_name *co = scalloc(sizeof(user_output_name), 1); | |
1038 | co->name = sstrdup(output_primary_name(output)); | |
1039 | TAILQ_INSERT_TAIL(list, co, user_output_names); | |
1040 | } | |
1041 | return; | |
1042 | } | |
1043 | ||
1044 | user_output_name *co = scalloc(sizeof(user_output_name), 1); | |
1045 | co->name = sstrdup(name); | |
1046 | TAILQ_INSERT_TAIL(list, co, user_output_names); | |
1047 | return; | |
1048 | } | |
1049 | ||
1050 | static Output *user_output_names_find_next(user_output_names_head *names, Output *current_output) { | |
1051 | Output *target_output = NULL; | |
1052 | user_output_name *uo; | |
1053 | TAILQ_FOREACH (uo, names, user_output_names) { | |
1054 | if (!target_output) { | |
1055 | /* The first available output from the list is used in 2 cases: | |
1056 | * 1. When we must wrap around the user list. For example, if user | |
1057 | * specifies outputs A B C and C is `current_output`. | |
1058 | * 2. When the current output is not in the user list. For example, | |
1059 | * user specifies A B C and D is `current_output`. */ | |
1060 | target_output = get_output_from_string(current_output, uo->name); | |
1061 | } | |
1062 | if (strcasecmp(output_primary_name(current_output), uo->name) == 0) { | |
1063 | /* The current output is in the user list */ | |
1064 | while (true) { | |
1065 | /* This corrupts the outer loop but it is ok since we are going | |
1066 | * to break anyway. */ | |
1067 | uo = TAILQ_NEXT(uo, user_output_names); | |
1068 | if (!uo) { | |
1069 | /* We reached the end of the list. We should use the first | |
1070 | * available output that, if it exists, is already saved in | |
1071 | * target_output. */ | |
1072 | break; | |
1073 | } | |
1074 | Output *out = get_output_from_string(current_output, uo->name); | |
1075 | if (out) { | |
1076 | return out; | |
1077 | } | |
1078 | } | |
1079 | break; | |
1080 | } | |
1081 | } | |
1082 | return target_output; | |
1083 | } | |
1084 | ||
1085 | static void user_output_names_free(user_output_names_head *names) { | |
1086 | user_output_name *uo; | |
1087 | while (!TAILQ_EMPTY(names)) { | |
1088 | uo = TAILQ_FIRST(names); | |
1089 | free(uo->name); | |
1090 | TAILQ_REMOVE(names, uo, user_output_names); | |
1091 | free(uo); | |
1092 | } | |
1093 | } | |
1094 | ||
1025 | 1095 | /* |
1026 | 1096 | * Implementation of 'move [window|container|workspace] [to] output <strings>'. |
1027 | 1097 | * |
1030 | 1100 | /* Initialize a data structure that is used to save multiple user-specified |
1031 | 1101 | * output names since this function is called multiple types for each |
1032 | 1102 | * command call. */ |
1033 | typedef struct user_output_name { | |
1034 | char *name; | |
1035 | TAILQ_ENTRY(user_output_name) user_output_names; | |
1036 | } user_output_name; | |
1037 | static TAILQ_HEAD(user_output_names_head, user_output_name) user_output_names = TAILQ_HEAD_INITIALIZER(user_output_names); | |
1103 | static user_output_names_head names = TAILQ_HEAD_INITIALIZER(names); | |
1038 | 1104 | |
1039 | 1105 | if (name) { |
1040 | if (strcmp(name, "next") == 0) { | |
1041 | /* "next" here works like a wildcard: It "expands" to all available | |
1042 | * outputs. */ | |
1043 | Output *output; | |
1044 | TAILQ_FOREACH (output, &outputs, outputs) { | |
1045 | user_output_name *co = scalloc(sizeof(user_output_name), 1); | |
1046 | co->name = sstrdup(output_primary_name(output)); | |
1047 | TAILQ_INSERT_TAIL(&user_output_names, co, user_output_names); | |
1048 | } | |
1049 | return; | |
1050 | } | |
1051 | ||
1052 | user_output_name *co = scalloc(sizeof(user_output_name), 1); | |
1053 | co->name = sstrdup(name); | |
1054 | TAILQ_INSERT_TAIL(&user_output_names, co, user_output_names); | |
1055 | return; | |
1056 | } | |
1057 | ||
1058 | HANDLE_EMPTY_MATCH; | |
1059 | ||
1060 | if (TAILQ_EMPTY(&user_output_names)) { | |
1106 | user_output_names_add(&names, name); | |
1107 | return; | |
1108 | } | |
1109 | ||
1110 | HANDLE_EMPTY_MATCH; | |
1111 | ||
1112 | if (TAILQ_EMPTY(&names)) { | |
1061 | 1113 | yerror("At least one output must be specified"); |
1062 | 1114 | return; |
1063 | 1115 | } |
1064 | 1116 | |
1065 | 1117 | bool success = false; |
1066 | user_output_name *uo; | |
1067 | 1118 | owindow *current; |
1068 | 1119 | TAILQ_FOREACH (current, &owindows, owindows) { |
1069 | 1120 | Con *ws = con_get_workspace(current->con); |
1072 | 1123 | } |
1073 | 1124 | |
1074 | 1125 | Output *current_output = get_output_for_con(ws); |
1075 | ||
1076 | Output *target_output = NULL; | |
1077 | TAILQ_FOREACH (uo, &user_output_names, user_output_names) { | |
1078 | if (strcasecmp(output_primary_name(current_output), uo->name) == 0) { | |
1079 | /* The current output is in the user list */ | |
1080 | while (true) { | |
1081 | /* This corrupts the outer loop but it is ok since we are | |
1082 | * going to break anyway. */ | |
1083 | uo = TAILQ_NEXT(uo, user_output_names); | |
1084 | if (!uo) { | |
1085 | /* We reached the end of the list. We should use the | |
1086 | * first available output that, if it exists, is | |
1087 | * already saved in target_output. */ | |
1088 | break; | |
1089 | } | |
1090 | Output *out = get_output_from_string(current_output, uo->name); | |
1091 | if (out) { | |
1092 | DLOG("Found next target for workspace %s from user list: %s\n", ws->name, uo->name); | |
1093 | target_output = out; | |
1094 | break; | |
1095 | } | |
1096 | } | |
1097 | break; | |
1098 | } | |
1099 | if (!target_output) { | |
1100 | /* The first available output from the list is used in 2 cases: | |
1101 | * 1. When we must wrap around the user list. For example, if | |
1102 | * user specifies outputs A B C and C is `current_output`. | |
1103 | * 2. When the current output is not in the user list. For | |
1104 | * example, user specifies A B C and D is `current_output`. | |
1105 | */ | |
1106 | DLOG("Found first target for workspace %s from user list: %s\n", ws->name, uo->name); | |
1107 | target_output = get_output_from_string(current_output, uo->name); | |
1108 | } | |
1109 | } | |
1126 | Output *target_output = user_output_names_find_next(&names, current_output); | |
1110 | 1127 | if (target_output) { |
1111 | 1128 | if (move_workspace) { |
1112 | 1129 | workspace_move_to_output(ws, target_output); |
1116 | 1133 | success = true; |
1117 | 1134 | } |
1118 | 1135 | } |
1119 | ||
1120 | while (!TAILQ_EMPTY(&user_output_names)) { | |
1121 | uo = TAILQ_FIRST(&user_output_names); | |
1122 | free(uo->name); | |
1123 | TAILQ_REMOVE(&user_output_names, uo, user_output_names); | |
1124 | free(uo); | |
1125 | } | |
1136 | user_output_names_free(&names); | |
1126 | 1137 | |
1127 | 1138 | cmd_output->needs_tree_render = success; |
1128 | 1139 | if (success) { |
1756 | 1767 | * |
1757 | 1768 | */ |
1758 | 1769 | void cmd_focus_output(I3_CMD, const char *name) { |
1770 | static user_output_names_head names = TAILQ_HEAD_INITIALIZER(names); | |
1771 | if (name) { | |
1772 | user_output_names_add(&names, name); | |
1773 | return; | |
1774 | } | |
1775 | ||
1776 | if (TAILQ_EMPTY(&names)) { | |
1777 | yerror("At least one output must be specified"); | |
1778 | return; | |
1779 | } | |
1780 | ||
1759 | 1781 | HANDLE_EMPTY_MATCH; |
1760 | 1782 | |
1761 | 1783 | if (TAILQ_EMPTY(&owindows)) { |
1764 | 1786 | } |
1765 | 1787 | |
1766 | 1788 | Output *current_output = get_output_for_con(TAILQ_FIRST(&owindows)->con); |
1767 | Output *output = get_output_from_string(current_output, name); | |
1768 | ||
1769 | if (!output) { | |
1770 | yerror("Output %s not found.", name); | |
1771 | return; | |
1772 | } | |
1773 | ||
1774 | /* get visible workspace on output */ | |
1775 | Con *ws = NULL; | |
1776 | GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); | |
1777 | if (!ws) { | |
1778 | yerror("BUG: No workspace found on output."); | |
1779 | return; | |
1780 | } | |
1781 | ||
1782 | workspace_show(ws); | |
1783 | ||
1784 | cmd_output->needs_tree_render = true; | |
1785 | ysuccess(true); | |
1789 | Output *target_output = user_output_names_find_next(&names, current_output); | |
1790 | user_output_names_free(&names); | |
1791 | bool success = false; | |
1792 | if (target_output) { | |
1793 | success = true; | |
1794 | ||
1795 | /* get visible workspace on output */ | |
1796 | Con *ws = NULL; | |
1797 | GREP_FIRST(ws, output_get_content(target_output->con), workspace_is_visible(child)); | |
1798 | if (!ws) { | |
1799 | yerror("BUG: No workspace found on output."); | |
1800 | return; | |
1801 | } | |
1802 | ||
1803 | workspace_show(ws); | |
1804 | } | |
1805 | ||
1806 | cmd_output->needs_tree_render = success; | |
1807 | if (success) { | |
1808 | ysuccess(true); | |
1809 | } else { | |
1810 | yerror("No output matched"); | |
1811 | } | |
1786 | 1812 | } |
1787 | 1813 | |
1788 | 1814 | /* |
2037 | 2063 | } |
2038 | 2064 | |
2039 | 2065 | /* |
2040 | * Implementation of 'title_window_icon <yes|no>' and 'title_window_icon padding <px>' | |
2066 | * Implementation of 'title_window_icon <yes|no|toggle>' and 'title_window_icon padding <px>' | |
2041 | 2067 | * |
2042 | 2068 | */ |
2043 | 2069 | void cmd_title_window_icon(I3_CMD, const char *enable, int padding) { |
2044 | if (enable != NULL && !boolstr(enable)) { | |
2045 | padding = -1; | |
2070 | bool is_toggle = false; | |
2071 | if (enable != NULL) { | |
2072 | if (strcmp(enable, "toggle") == 0) { | |
2073 | is_toggle = true; | |
2074 | } else if (!boolstr(enable)) { | |
2075 | padding = -1; | |
2076 | } | |
2046 | 2077 | } |
2047 | 2078 | DLOG("setting window_icon=%d\n", padding); |
2048 | 2079 | HANDLE_EMPTY_MATCH; |
2049 | 2080 | |
2050 | 2081 | owindow *current; |
2051 | 2082 | TAILQ_FOREACH (current, &owindows, owindows) { |
2052 | DLOG("setting window_icon for %p / %s\n", current->con, current->con->name); | |
2053 | current->con->window_icon_padding = padding; | |
2083 | if (is_toggle) { | |
2084 | const int current_padding = current->con->window_icon_padding; | |
2085 | if (padding > 0) { | |
2086 | if (current_padding < 0) { | |
2087 | current->con->window_icon_padding = padding; | |
2088 | } else { | |
2089 | /* toggle off, but store padding given */ | |
2090 | current->con->window_icon_padding = -(padding + 1); | |
2091 | } | |
2092 | } else { | |
2093 | /* Set to negative of (current value+1) to keep old padding when toggling */ | |
2094 | current->con->window_icon_padding = -(current_padding + 1); | |
2095 | } | |
2096 | } else { | |
2097 | current->con->window_icon_padding = padding; | |
2098 | } | |
2099 | DLOG("Set window_icon for %p / %s to %d\n", current->con, current->con->name, current->con->window_icon_padding); | |
2054 | 2100 | |
2055 | 2101 | if (current->con->window != NULL) { |
2056 | 2102 | /* Make sure the window title is redrawn immediately. */ |
389 | 389 | free(possible_tokens); |
390 | 390 | |
391 | 391 | /* Contains the same amount of characters as 'input' has, but with |
392 | * the unparseable part highlighted using ^ characters. */ | |
392 | * the unparsable part highlighted using ^ characters. */ | |
393 | 393 | char *position = smalloc(len + 1); |
394 | 394 | for (const char *copywalk = input; *copywalk != '\0'; copywalk++) |
395 | 395 | position[(copywalk - input)] = (copywalk >= walk ? '^' : ' '); |
730 | 730 | } |
731 | 731 | |
732 | 732 | /* |
733 | * Start from a container and traverse the transient_for linked list. Returns | |
734 | * true if target window is found in the list. Protects againsts potential | |
735 | * cycles. | |
736 | * | |
737 | */ | |
738 | bool con_find_transient_for_window(Con *start, xcb_window_t target) { | |
739 | Con *transient_con = start; | |
740 | int count = con_num_windows(croot); | |
741 | while (transient_con != NULL && | |
742 | transient_con->window != NULL && | |
743 | transient_con->window->transient_for != XCB_NONE) { | |
744 | DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, target = 0x%08x\n", | |
745 | transient_con->window->id, transient_con->window->transient_for, target); | |
746 | if (transient_con->window->transient_for == target) { | |
747 | return true; | |
748 | } | |
749 | Con *next_transient = con_by_window_id(transient_con->window->transient_for); | |
750 | if (next_transient == NULL) { | |
751 | break; | |
752 | } | |
753 | /* Some clients (e.g. x11-ssh-askpass) actually set WM_TRANSIENT_FOR to | |
754 | * their own window id, so break instead of looping endlessly. */ | |
755 | if (transient_con == next_transient) { | |
756 | break; | |
757 | } | |
758 | transient_con = next_transient; | |
759 | ||
760 | if (count-- <= 0) { /* Avoid cycles, see #4404 */ | |
761 | break; | |
762 | } | |
763 | } | |
764 | return false; | |
765 | } | |
766 | ||
767 | /* | |
733 | 768 | * Returns true if and only if the given containers holds the mark. |
734 | 769 | * |
735 | 770 | */ |
851 | 886 | Con *con_for_window(Con *con, i3Window *window, Match **store_match) { |
852 | 887 | Con *child; |
853 | 888 | Match *match; |
854 | //DLOG("searching con for window %p starting at con %p\n", window, con); | |
855 | //DLOG("class == %s\n", window->class_class); | |
856 | 889 | |
857 | 890 | TAILQ_FOREACH (child, &(con->nodes_head), nodes) { |
858 | 891 | TAILQ_FOREACH (match, &(child->swallow_head), matches) { |
1011 | 1044 | Con *child; |
1012 | 1045 | int children = con_num_children(con); |
1013 | 1046 | |
1014 | // calculate how much we have distributed and how many containers | |
1015 | // with a percentage set we have | |
1047 | /* calculate how much we have distributed and how many containers with a | |
1048 | * percentage set we have */ | |
1016 | 1049 | double total = 0.0; |
1017 | 1050 | int children_with_percent = 0; |
1018 | 1051 | TAILQ_FOREACH (child, &(con->nodes_head), nodes) { |
1022 | 1055 | } |
1023 | 1056 | } |
1024 | 1057 | |
1025 | // if there were children without a percentage set, set to a value that | |
1026 | // will make those children proportional to all others | |
1058 | /* if there were children without a percentage set, set to a value that | |
1059 | * will make those children proportional to all others */ | |
1027 | 1060 | if (children_with_percent != children) { |
1028 | 1061 | TAILQ_FOREACH (child, &(con->nodes_head), nodes) { |
1029 | 1062 | if (child->percent <= 0.0) { |
1036 | 1069 | } |
1037 | 1070 | } |
1038 | 1071 | |
1039 | // if we got a zero, just distribute the space equally, otherwise | |
1040 | // distribute according to the proportions we got | |
1072 | /* if we got a zero, just distribute the space equally, otherwise | |
1073 | * distribute according to the proportions we got */ | |
1041 | 1074 | if (total == 0.0) { |
1042 | 1075 | TAILQ_FOREACH (child, &(con->nodes_head), nodes) { |
1043 | 1076 | child->percent = 1.0 / children; |
1355 | 1388 | return true; |
1356 | 1389 | } |
1357 | 1390 | |
1358 | /* | |
1359 | * Moves the given container to the given mark. | |
1360 | * | |
1361 | */ | |
1362 | bool con_move_to_mark(Con *con, const char *mark) { | |
1363 | Con *target = con_by_mark(mark); | |
1364 | if (target == NULL) { | |
1365 | DLOG("found no container with mark \"%s\"\n", mark); | |
1366 | return false; | |
1367 | } | |
1368 | ||
1391 | bool con_move_to_target(Con *con, Con *target) { | |
1369 | 1392 | /* For target containers in the scratchpad, we just send the window to the scratchpad. */ |
1370 | 1393 | if (con_get_workspace(target) == workspace_get("__i3_scratch")) { |
1371 | 1394 | DLOG("target container is in the scratchpad, moving container to scratchpad.\n"); |
1400 | 1423 | } |
1401 | 1424 | |
1402 | 1425 | return _con_move_to_con(con, target, false, true, false, false, true); |
1426 | } | |
1427 | ||
1428 | /* | |
1429 | * Moves the given container to the given mark. | |
1430 | * | |
1431 | */ | |
1432 | bool con_move_to_mark(Con *con, const char *mark) { | |
1433 | Con *target = con_by_mark(mark); | |
1434 | if (target == NULL) { | |
1435 | DLOG("found no container with mark \"%s\"\n", mark); | |
1436 | return false; | |
1437 | } | |
1438 | ||
1439 | return con_move_to_target(con, target); | |
1403 | 1440 | } |
1404 | 1441 | |
1405 | 1442 | /* |
2201 | 2238 | } else |
2202 | 2239 | DLOG("Discarding urgency WM_HINT because timer is running\n"); |
2203 | 2240 | |
2204 | //CLIENT_LOG(con); | |
2205 | 2241 | if (con->window) { |
2206 | 2242 | if (con->urgent) { |
2207 | 2243 | gettimeofday(&con->window->urgent, NULL); |
196 | 196 | INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50"); |
197 | 197 | INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e"); |
198 | 198 | INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff", "#900000"); |
199 | config.client.got_focused_tab_title = false; | |
199 | 200 | |
200 | 201 | /* border and indicator color are ignored for placeholder contents */ |
201 | 202 | INIT_COLOR(config.client.placeholder, "#000000", "#0c0c0c", "#ffffff", "#000000"); |
271 | 272 | set_font(&config.font); |
272 | 273 | } |
273 | 274 | |
275 | /* Make bar config blocks without a configured font use the i3-wide font. */ | |
276 | Barconfig *current; | |
277 | TAILQ_FOREACH (current, &barconfigs, configs) { | |
278 | if (current->font != NULL) { | |
279 | continue; | |
280 | } | |
281 | current->font = sstrdup(config.font.pattern); | |
282 | } | |
283 | ||
274 | 284 | if (load_type == C_RELOAD) { |
275 | 285 | translate_keysyms(); |
276 | 286 | grab_all_keys(conn); |
162 | 162 | return result; |
163 | 163 | } |
164 | 164 | |
165 | static char *font_pattern; | |
166 | ||
167 | 165 | CFGFUN(font, const char *font) { |
168 | 166 | config.font = load_font(font, true); |
169 | 167 | set_font(&config.font); |
170 | ||
171 | /* Save the font pattern for using it as bar font later on */ | |
172 | FREE(font_pattern); | |
173 | font_pattern = sstrdup(font); | |
174 | 168 | } |
175 | 169 | |
176 | 170 | CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command) { |
185 | 179 | static bool current_mode_pango_markup; |
186 | 180 | |
187 | 181 | CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command) { |
182 | if (current_mode == NULL) { | |
183 | /* When using an invalid mode name, e.g. “default” */ | |
184 | return; | |
185 | } | |
186 | ||
188 | 187 | configure_binding(bindtype, modifiers, key, release, border, whole_window, exclude_titlebar, command, current_mode, current_mode_pango_markup); |
189 | 188 | } |
190 | 189 | |
466 | 465 | } |
467 | 466 | |
468 | 467 | CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border) { |
469 | #define APPLY_COLORS(classname) \ | |
470 | do { \ | |
471 | if (strcmp(colorclass, "client." #classname) == 0) { \ | |
472 | config.client.classname.border = draw_util_hex_to_color(border); \ | |
473 | config.client.classname.background = draw_util_hex_to_color(background); \ | |
474 | config.client.classname.text = draw_util_hex_to_color(text); \ | |
475 | if (indicator != NULL) { \ | |
476 | config.client.classname.indicator = draw_util_hex_to_color(indicator); \ | |
477 | } \ | |
478 | if (child_border != NULL) { \ | |
479 | config.client.classname.child_border = draw_util_hex_to_color(child_border); \ | |
480 | } else { \ | |
481 | config.client.classname.child_border = config.client.classname.background; \ | |
482 | } \ | |
483 | } \ | |
468 | #define APPLY_COLORS(classname) \ | |
469 | do { \ | |
470 | if (strcmp(colorclass, "client." #classname) == 0) { \ | |
471 | if (strcmp("focused_tab_title", #classname) == 0) { \ | |
472 | config.client.got_focused_tab_title = true; \ | |
473 | if (indicator || child_border) { \ | |
474 | ELOG("indicator and child_border colors have no effect for client.focused_tab_title\n"); \ | |
475 | } \ | |
476 | } \ | |
477 | config.client.classname.border = draw_util_hex_to_color(border); \ | |
478 | config.client.classname.background = draw_util_hex_to_color(background); \ | |
479 | config.client.classname.text = draw_util_hex_to_color(text); \ | |
480 | if (indicator != NULL) { \ | |
481 | config.client.classname.indicator = draw_util_hex_to_color(indicator); \ | |
482 | } \ | |
483 | if (child_border != NULL) { \ | |
484 | config.client.classname.child_border = draw_util_hex_to_color(child_border); \ | |
485 | } else { \ | |
486 | config.client.classname.child_border = config.client.classname.background; \ | |
487 | } \ | |
488 | return; \ | |
489 | } \ | |
484 | 490 | } while (0) |
485 | 491 | |
486 | 492 | APPLY_COLORS(focused_inactive); |
493 | APPLY_COLORS(focused_tab_title); | |
487 | 494 | APPLY_COLORS(focused); |
488 | 495 | APPLY_COLORS(unfocused); |
489 | 496 | APPLY_COLORS(urgent); |
743 | 750 | |
744 | 751 | config.number_barconfigs++; |
745 | 752 | |
746 | /* If no font was explicitly set, we use the i3 font as default */ | |
747 | if (current_bar->font == NULL && font_pattern != NULL) | |
748 | current_bar->font = sstrdup(font_pattern); | |
749 | ||
750 | 753 | TAILQ_INSERT_TAIL(&barconfigs, current_bar, configs); |
751 | 754 | /* Simply reset the pointer, but don't free the resources. */ |
752 | 755 | current_bar = NULL; |
164 | 164 | static void next_state(const cmdp_token *token, struct parser_ctx *ctx) { |
165 | 165 | cmdp_state _next_state = token->next_state; |
166 | 166 | |
167 | //printf("token = name %s identifier %s\n", token->name, token->identifier); | |
168 | //printf("next_state = %d\n", token->next_state); | |
169 | 167 | if (token->next_state == __CALL) { |
170 | 168 | struct ConfigResultIR subcommand_output = { |
171 | 169 | .ctx = ctx, |
253 | 251 | bool token_handled; |
254 | 252 | linecnt = 1; |
255 | 253 | |
256 | // TODO: make this testable | |
257 | 254 | #ifndef TEST_PARSER |
258 | 255 | struct ConfigResultIR subcommand_output = { |
259 | 256 | .ctx = ctx, |
268 | 265 | * separate configuration directives. */ |
269 | 266 | while ((*walk == ' ' || *walk == '\t') && *walk != '\0') |
270 | 267 | walk++; |
271 | ||
272 | //printf("remaining input: %s\n", walk); | |
273 | 268 | |
274 | 269 | cmdp_token_ptr *ptr = &(tokens[ctx->state]); |
275 | 270 | token_handled = false; |
377 | 372 | } |
378 | 373 | |
379 | 374 | if (strcmp(token->name, "end") == 0) { |
380 | //printf("checking for end: *%s*\n", walk); | |
381 | 375 | if (*walk == '\0' || *walk == '\n' || *walk == '\r') { |
382 | 376 | next_state(token, ctx); |
383 | 377 | token_handled = true; |
385 | 379 | * datastructure for commands which do *not* specify any |
386 | 380 | * criteria, we re-initialize the criteria system after |
387 | 381 | * every command. */ |
388 | // TODO: make this testable | |
389 | 382 | #ifndef TEST_PARSER |
390 | 383 | cfg_criteria_init(&(ctx->current_match), &subcommand_output, INITIAL); |
391 | 384 | #endif |
444 | 437 | const char *error_line = start_of_line(walk, input); |
445 | 438 | |
446 | 439 | /* Contains the same amount of characters as 'input' has, but with |
447 | * the unparseable part highlighted using ^ characters. */ | |
440 | * the unparsable part highlighted using ^ characters. */ | |
448 | 441 | char *position = scalloc(strlen(error_line) + 1, 1); |
449 | 442 | const char *copywalk; |
450 | 443 | for (copywalk = error_line; |
1007 | 1000 | char *next; |
1008 | 1001 | for (next = bufcopy; |
1009 | 1002 | next < (bufcopy + stbuf.st_size) && |
1010 | (next = strcasestr(next, current->key)) != NULL; | |
1011 | next += strlen(current->key)) { | |
1012 | *next = '_'; | |
1003 | (next = strcasestr(next, current->key)) != NULL;) { | |
1004 | /* We need to invalidate variables completely (otherwise we may count | |
1005 | * the same variable more than once, thus causing buffer overflow or | |
1006 | * allocation failure) with spaces (variable names cannot contain spaces) */ | |
1007 | char *end = next + strlen(current->key); | |
1008 | while (next < end) { | |
1009 | *next++ = ' '; | |
1010 | } | |
1013 | 1011 | extra_bytes += extra; |
1014 | 1012 | } |
1015 | 1013 | } |
68 | 68 | /* |
69 | 69 | * Connects to i3 to find out the currently running version. Useful since it |
70 | 70 | * might be different from the version compiled into this binary (maybe the |
71 | * user didn’t correctly install i3 or forgot te restart it). | |
71 | * user didn’t correctly install i3 or forgot to restart it). | |
72 | 72 | * |
73 | 73 | * The output looks like this: |
74 | 74 | * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) |
252 | 252 | } |
253 | 253 | /* Consider the part of the focus stack of our current workspace: |
254 | 254 | * [ ... S_{i-1} S_{i} S_{i+1} ... ] |
255 | * Where S_{x} is a container tree and the container 'con' that is beeing switched to | |
255 | * Where S_{x} is a container tree and the container 'con' that is being switched to | |
256 | 256 | * floating belongs in S_{i}. The new floating container, 'nc', will have the |
257 | 257 | * workspace as its parent so it needs to be placed in this stack. If C was focused |
258 | 258 | * we just need to call con_focus(). Otherwise, nc must be placed before or after S_{i}. |
69 | 69 | event->response_type != response_type) |
70 | 70 | continue; |
71 | 71 | |
72 | /* instead of removing a sequence number we better wait until it gets | |
73 | * garbage collected. it may generate multiple events (there are multiple | |
74 | * enter_notifies for one configure_request, for example). */ | |
75 | //SLIST_REMOVE(&ignore_events, event, Ignore_Event, ignore_events); | |
76 | //free(event); | |
72 | /* Instead of removing & freeing a sequence number we better wait until | |
73 | * it gets garbage collected. It may generate multiple events (there | |
74 | * are multiple enter_notifies for one configure_request, for example). */ | |
77 | 75 | return true; |
78 | 76 | } |
79 | 77 | |
269 | 267 | * Configure requests are received when the application wants to resize windows |
270 | 268 | * on their own. |
271 | 269 | * |
272 | * We generate a synthethic configure notify event to signalize the client its | |
270 | * We generate a synthetic configure notify event to signalize the client its | |
273 | 271 | * "new" position. |
274 | 272 | * |
275 | 273 | */ |
800 | 798 | } else if (event->type == A_WM_CHANGE_STATE) { |
801 | 799 | /* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */ |
802 | 800 | if (event->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) { |
803 | /* For compatiblity reasons, Wine will request iconic state and cannot ensure that the WM has agreed on it; | |
801 | /* For compatibility reasons, Wine will request iconic state and cannot ensure that the WM has agreed on it; | |
804 | 802 | * immediately revert to normal to avoid being stuck in a paused state. */ |
805 | 803 | DLOG("Client has requested iconic state, rejecting. (window = %08x)\n", event->window); |
806 | 804 | long data[] = {XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE}; |
1183 | 1181 | } |
1184 | 1182 | |
1185 | 1183 | /* |
1186 | * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings. | |
1184 | * Handles the _MOTIF_WM_HINTS property of specifying window deocration settings. | |
1187 | 1185 | * |
1188 | 1186 | */ |
1189 | 1187 | static bool handle_motif_hints_change(Con *con, xcb_get_property_reply_t *prop) { |
1190 | 1188 | border_style_t motif_border_style; |
1191 | window_update_motif_hints(con->window, prop, &motif_border_style); | |
1192 | ||
1193 | if (motif_border_style != con->border_style && motif_border_style != BS_NORMAL) { | |
1189 | bool has_mwm_hints = window_update_motif_hints(con->window, prop, &motif_border_style); | |
1190 | ||
1191 | if (has_mwm_hints && motif_border_style != con->border_style) { | |
1194 | 1192 | DLOG("Update border style of con %p to %d\n", con, motif_border_style); |
1195 | 1193 | con_set_border_style(con, motif_border_style, con->current_border_width); |
1196 | 1194 | |
1345 | 1343 | } |
1346 | 1344 | |
1347 | 1345 | if (handler == NULL) { |
1348 | //DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom); | |
1346 | /* DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom); */ | |
1349 | 1347 | return; |
1350 | 1348 | } |
1351 | 1349 | |
1523 | 1521 | break; |
1524 | 1522 | |
1525 | 1523 | default: |
1526 | //DLOG("Unhandled event of type %d\n", type); | |
1527 | break; | |
1528 | } | |
1529 | } | |
1524 | /* DLOG("Unhandled event of type %d\n", type); */ | |
1525 | break; | |
1526 | } | |
1527 | } |
136 | 136 | * For 512 MiB of RAM this will lead to a 5 MiB log buffer. |
137 | 137 | * At the moment (2011-12-10), no testcase leads to an i3 log |
138 | 138 | * of more than ~ 600 KiB. */ |
139 | logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size); | |
139 | logbuffer_size = shmlog_size; | |
140 | if (physical_mem_bytes * 0.01 < (long long)shmlog_size) { | |
141 | logbuffer_size = physical_mem_bytes * 0.01; | |
142 | } | |
143 | ||
140 | 144 | #if defined(__FreeBSD__) |
141 | 145 | sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid()); |
142 | 146 | #else |
682 | 682 | else |
683 | 683 | config.ipc_socket_path = sstrdup(config.ipc_socket_path); |
684 | 684 | } |
685 | /* Create the UNIX domain socket for IPC */ | |
686 | int ipc_socket = create_socket(config.ipc_socket_path, ¤t_socketpath); | |
687 | if (ipc_socket == -1) { | |
688 | die("Could not create the IPC socket: %s", config.ipc_socket_path); | |
689 | } | |
685 | 690 | |
686 | 691 | if (config.force_xinerama) { |
687 | 692 | force_xinerama = true; |
690 | 695 | /* Acquire the WM_Sn selection. */ |
691 | 696 | { |
692 | 697 | /* Get the WM_Sn atom */ |
693 | char *atom_name = xcb_atom_name_by_screen("WM_S", conn_screen); | |
698 | char *atom_name = xcb_atom_name_by_screen("WM", conn_screen); | |
694 | 699 | wm_sn_selection_owner = xcb_generate_id(conn); |
695 | 700 | |
696 | 701 | if (atom_name == NULL) { |
697 | ELOG("xcb_atom_name_by_screen(\"WM_S\", %d) failed, exiting\n", conn_screen); | |
702 | ELOG("xcb_atom_name_by_screen(\"WM\", %d) failed, exiting\n", conn_screen); | |
698 | 703 | return 1; |
699 | 704 | } |
700 | 705 | |
987 | 992 | |
988 | 993 | tree_render(); |
989 | 994 | |
990 | /* Create the UNIX domain socket for IPC */ | |
991 | int ipc_socket = create_socket(config.ipc_socket_path, ¤t_socketpath); | |
992 | if (ipc_socket == -1) { | |
993 | ELOG("Could not create the IPC socket, IPC disabled\n"); | |
994 | } else { | |
995 | struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io)); | |
996 | ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); | |
997 | ev_io_start(main_loop, ipc_io); | |
998 | } | |
995 | /* Listen to the IPC socket for clients */ | |
996 | struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io)); | |
997 | ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); | |
998 | ev_io_start(main_loop, ipc_io); | |
999 | 999 | |
1000 | 1000 | /* Chose a file name in /tmp/ based on the PID */ |
1001 | 1001 | char *log_stream_socket_path = get_process_filename("log-stream-socket"); |
122 | 122 | |
123 | 123 | geomc = xcb_get_geometry(conn, d); |
124 | 124 | |
125 | /* Check if the window is mapped (it could be not mapped when intializing and | |
125 | /* Check if the window is mapped (it could be not mapped when initializing and | |
126 | 126 | calling manage_window() for every window) */ |
127 | 127 | if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) { |
128 | 128 | DLOG("Could not get attributes\n"); |
213 | 213 | window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL)); |
214 | 214 | bool urgency_hint; |
215 | 215 | window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint); |
216 | border_style_t motif_border_style = BS_NORMAL; | |
217 | window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style); | |
216 | border_style_t motif_border_style; | |
217 | bool has_mwm_hints = window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style); | |
218 | 218 | window_update_normal_hints(cwindow, xcb_get_property_reply(conn, wm_normal_hints_cookie, NULL), geom); |
219 | 219 | window_update_machine(cwindow, xcb_get_property_reply(conn, wm_machine_cookie, NULL)); |
220 | 220 | xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL); |
484 | 484 | (cwindow->leader != XCB_NONE && |
485 | 485 | cwindow->leader != cwindow->id && |
486 | 486 | con_by_window_id(cwindow->leader) != NULL)) { |
487 | LOG("This window is transient for another window, setting floating\n"); | |
487 | DLOG("This window is transient for another window, setting floating\n"); | |
488 | 488 | want_floating = true; |
489 | 489 | |
490 | 490 | if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && |
491 | 491 | fs != NULL) { |
492 | LOG("There is a fullscreen window, leaving fullscreen mode\n"); | |
492 | DLOG("There is a fullscreen window, leaving fullscreen mode\n"); | |
493 | 493 | con_toggle_fullscreen(fs, CF_OUTPUT); |
494 | 494 | } else if (config.popup_during_fullscreen == PDF_SMART && |
495 | 495 | fs != NULL && |
496 | 496 | fs->window != NULL) { |
497 | i3Window *transient_win = cwindow; | |
498 | while (transient_win != NULL && | |
499 | transient_win->transient_for != XCB_NONE) { | |
500 | if (transient_win->transient_for == fs->window->id) { | |
501 | LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n"); | |
502 | set_focus = true; | |
503 | break; | |
504 | } | |
505 | Con *next_transient = con_by_window_id(transient_win->transient_for); | |
506 | if (next_transient == NULL) | |
507 | break; | |
508 | /* Some clients (e.g. x11-ssh-askpass) actually set | |
509 | * WM_TRANSIENT_FOR to their own window id, so break instead of | |
510 | * looping endlessly. */ | |
511 | if (transient_win == next_transient->window) | |
512 | break; | |
513 | transient_win = next_transient->window; | |
514 | } | |
497 | set_focus = con_find_transient_for_window(nc, fs->window->id); | |
515 | 498 | } |
516 | 499 | } |
517 | 500 | |
527 | 510 | if (nc->geometry.width == 0) |
528 | 511 | nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height}; |
529 | 512 | |
530 | if (motif_border_style != BS_NORMAL) { | |
513 | if (has_mwm_hints) { | |
531 | 514 | DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style); |
532 | 515 | if (want_floating) { |
533 | 516 | con_set_border_style(nc, motif_border_style, config.default_floating_border_width); |
227 | 227 | * the focused container, con, is now a child of ws. To work around this |
228 | 228 | * and still produce the correct workspace focus events (see |
229 | 229 | * 517-regress-move-direction-ipc.t) we need to temporarily set focused |
230 | * to the old workspace. */ | |
230 | * to the old workspace. | |
231 | * | |
232 | * The following happen: | |
233 | * 1. Focus con to push it on the top of the focus stack in its new | |
234 | * workspace | |
235 | * 2. Set focused to the old workspace to force workspace_show to | |
236 | * execute | |
237 | * 3. workspace_show will descend focus and target our con for | |
238 | * focusing. This also ensures that the mouse warps correctly. | |
239 | * See: #3518. */ | |
240 | con_focus(con); | |
231 | 241 | focused = old_ws; |
232 | 242 | workspace_show(ws); |
233 | con_focus(con); | |
234 | 243 | } |
235 | 244 | |
236 | 245 | /* force re-painting the indicators */ |
319 | 328 | } else { |
320 | 329 | TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes); |
321 | 330 | } |
331 | ||
332 | /* redraw parents to ensure all parent split container titles are updated correctly */ | |
333 | con_force_split_parents_redraw(con); | |
322 | 334 | |
323 | 335 | ipc_send_window_event("move", con); |
324 | 336 | return; |
664 | 664 | |
665 | 665 | new->primary = monitor_info->primary; |
666 | 666 | |
667 | new->changed = | |
668 | update_if_necessary(&(new->rect.x), monitor_info->x) | | |
669 | update_if_necessary(&(new->rect.y), monitor_info->y) | | |
670 | update_if_necessary(&(new->rect.width), monitor_info->width) | | |
671 | update_if_necessary(&(new->rect.height), monitor_info->height); | |
667 | const bool update_x = update_if_necessary(&(new->rect.x), monitor_info->x); | |
668 | const bool update_y = update_if_necessary(&(new->rect.y), monitor_info->y); | |
669 | const bool update_w = update_if_necessary(&(new->rect.width), monitor_info->width); | |
670 | const bool update_h = update_if_necessary(&(new->rect.height), monitor_info->height); | |
671 | ||
672 | new->changed = update_x || update_y || update_w || update_h; | |
672 | 673 | |
673 | 674 | DLOG("name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n", |
674 | 675 | name, |
742 | 743 | return; |
743 | 744 | } |
744 | 745 | |
745 | bool updated = update_if_necessary(&(new->rect.x), crtc->x) | | |
746 | update_if_necessary(&(new->rect.y), crtc->y) | | |
747 | update_if_necessary(&(new->rect.width), crtc->width) | | |
748 | update_if_necessary(&(new->rect.height), crtc->height); | |
746 | const bool update_x = update_if_necessary(&(new->rect.x), crtc->x); | |
747 | const bool update_y = update_if_necessary(&(new->rect.y), crtc->y); | |
748 | const bool update_w = update_if_necessary(&(new->rect.width), crtc->width); | |
749 | const bool update_h = update_if_necessary(&(new->rect.height), crtc->height); | |
750 | const bool updated = update_x || update_y || update_w || update_h; | |
749 | 751 | free(crtc); |
750 | 752 | new->active = (new->rect.width != 0 && new->rect.height != 0); |
751 | 753 | if (!new->active) { |
942 | 944 | uint32_t width = min(other->rect.width, output->rect.width); |
943 | 945 | uint32_t height = min(other->rect.height, output->rect.height); |
944 | 946 | |
945 | if (update_if_necessary(&(output->rect.width), width) | | |
946 | update_if_necessary(&(output->rect.height), height)) | |
947 | const bool update_w = update_if_necessary(&(output->rect.width), width); | |
948 | const bool update_h = update_if_necessary(&(output->rect.height), height); | |
949 | if (update_w || update_h) { | |
947 | 950 | output->changed = true; |
951 | } | |
948 | 952 | |
949 | 953 | update_if_necessary(&(other->rect.width), width); |
950 | 954 | update_if_necessary(&(other->rect.height), height); |
19 | 19 | * |
20 | 20 | */ |
21 | 21 | struct regex *regex_new(const char *pattern) { |
22 | const char *error; | |
23 | int errorcode, offset; | |
22 | int errorcode; | |
23 | PCRE2_SIZE offset; | |
24 | 24 | |
25 | 25 | struct regex *re = scalloc(1, sizeof(struct regex)); |
26 | 26 | re->pattern = sstrdup(pattern); |
27 | int options = PCRE_UTF8; | |
27 | uint32_t options = PCRE2_UTF; | |
28 | 28 | /* We use PCRE_UCP so that \B, \b, \D, \d, \S, \s, \W, \w and some POSIX |
29 | 29 | * character classes play nicely with Unicode */ |
30 | options |= PCRE_UCP; | |
31 | while (!(re->regex = pcre_compile2(pattern, options, &errorcode, &error, &offset, NULL))) { | |
32 | /* If the error is that PCRE was not compiled with UTF-8 support we | |
33 | * disable it and try again */ | |
34 | if (errorcode == 32) { | |
35 | options &= ~PCRE_UTF8; | |
36 | continue; | |
37 | } | |
38 | ELOG("PCRE regular expression compilation failed at %d: %s\n", | |
39 | offset, error); | |
30 | options |= PCRE2_UCP; | |
31 | if (!(re->regex = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options, &errorcode, &offset, NULL))) { | |
32 | PCRE2_UCHAR buffer[256]; | |
33 | pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); | |
34 | ELOG("PCRE regular expression compilation failed at %lu: %s\n", | |
35 | offset, buffer); | |
40 | 36 | regex_free(re); |
41 | 37 | return NULL; |
42 | } | |
43 | re->extra = pcre_study(re->regex, 0, &error); | |
44 | /* If an error happened, we print the error message, but continue. | |
45 | * Studying the regular expression leads to faster matching, but it’s not | |
46 | * absolutely necessary. */ | |
47 | if (error) { | |
48 | ELOG("PCRE regular expression studying failed: %s\n", error); | |
49 | 38 | } |
50 | 39 | return re; |
51 | 40 | } |
59 | 48 | return; |
60 | 49 | FREE(regex->pattern); |
61 | 50 | FREE(regex->regex); |
62 | FREE(regex->extra); | |
63 | 51 | FREE(regex); |
64 | 52 | } |
65 | 53 | |
70 | 58 | * |
71 | 59 | */ |
72 | 60 | bool regex_matches(struct regex *regex, const char *input) { |
61 | pcre2_match_data *match_data; | |
73 | 62 | int rc; |
63 | ||
64 | match_data = pcre2_match_data_create_from_pattern(regex->regex, NULL); | |
74 | 65 | |
75 | 66 | /* We use strlen() because pcre_exec() expects the length of the input |
76 | 67 | * string in bytes */ |
77 | if ((rc = pcre_exec(regex->regex, regex->extra, input, strlen(input), 0, 0, NULL, 0)) == 0) { | |
68 | rc = pcre2_match(regex->regex, (PCRE2_SPTR)input, strlen(input), 0, 0, match_data, NULL); | |
69 | pcre2_match_data_free(match_data); | |
70 | if (rc > 0) { | |
78 | 71 | LOG("Regular expression \"%s\" matches \"%s\"\n", |
79 | 72 | regex->pattern, input); |
80 | 73 | return true; |
81 | 74 | } |
82 | 75 | |
83 | if (rc == PCRE_ERROR_NOMATCH) { | |
76 | if (rc == PCRE2_ERROR_NOMATCH) { | |
84 | 77 | LOG("Regular expression \"%s\" does not match \"%s\"\n", |
85 | 78 | regex->pattern, input); |
86 | 79 | return false; |
225 | 225 | } |
226 | 226 | |
227 | 227 | Con *floating_child = con_descend_focused(child); |
228 | Con *transient_con = floating_child; | |
229 | bool is_transient_for = false; | |
230 | while (transient_con != NULL && | |
231 | transient_con->window != NULL && | |
232 | transient_con->window->transient_for != XCB_NONE) { | |
233 | DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n", | |
234 | transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id); | |
235 | if (transient_con->window->transient_for == fullscreen->window->id) { | |
236 | is_transient_for = true; | |
237 | break; | |
238 | } | |
239 | Con *next_transient = con_by_window_id(transient_con->window->transient_for); | |
240 | if (next_transient == NULL) | |
241 | break; | |
242 | /* Some clients (e.g. x11-ssh-askpass) actually set | |
243 | * WM_TRANSIENT_FOR to their own window id, so break instead of | |
244 | * looping endlessly. */ | |
245 | if (transient_con == next_transient) | |
246 | break; | |
247 | transient_con = next_transient; | |
248 | } | |
249 | ||
250 | if (!is_transient_for) | |
251 | continue; | |
252 | else { | |
228 | if (con_find_transient_for_window(floating_child, fullscreen->window->id)) { | |
253 | 229 | DLOG("Rendering floating child even though in fullscreen mode: " |
254 | 230 | "floating->transient_for (0x%08x) --> fullscreen->id (0x%08x)\n", |
255 | 231 | floating_child->window->transient_for, fullscreen->window->id); |
232 | } else { | |
233 | continue; | |
256 | 234 | } |
257 | 235 | } |
258 | 236 | DLOG("floating child at (%d,%d) with %d x %d\n", |
0 | /* | |
1 | * vim:ts=4:sw=4:expandtab | |
2 | * | |
3 | * i3 - an improved dynamic tiling window manager | |
4 | * © 2009 Michael Stapelberg and contributors (see also: LICENSE) | |
5 | * | |
6 | * tiling_drag.c: Reposition tiled windows by dragging. | |
7 | * | |
8 | */ | |
9 | #include "all.h" | |
10 | static xcb_window_t create_drop_indicator(Rect rect); | |
11 | ||
12 | /* | |
13 | * Includes decoration (container title) to the container's rect. This way we | |
14 | * can find the correct drop target if the mouse is on a container's | |
15 | * decoration. | |
16 | * | |
17 | */ | |
18 | static Rect con_rect_plus_deco_height(Con *con) { | |
19 | Rect rect = con->rect; | |
20 | rect.height += con->deco_rect.height; | |
21 | if (rect.y < con->deco_rect.height) { | |
22 | rect.y = 0; | |
23 | } else { | |
24 | rect.y -= con->deco_rect.height; | |
25 | } | |
26 | return rect; | |
27 | } | |
28 | ||
29 | /* | |
30 | * Return an appropriate target at given coordinates. | |
31 | * | |
32 | */ | |
33 | static Con *find_drop_target(uint32_t x, uint32_t y) { | |
34 | Con *con; | |
35 | TAILQ_FOREACH (con, &all_cons, all_cons) { | |
36 | Rect rect = con_rect_plus_deco_height(con); | |
37 | ||
38 | if (rect_contains(rect, x, y) && | |
39 | con_has_managed_window(con) && | |
40 | !con_is_floating(con) && | |
41 | !con_is_hidden(con)) { | |
42 | Con *ws = con_get_workspace(con); | |
43 | if (!workspace_is_visible(ws)) { | |
44 | continue; | |
45 | } | |
46 | Con *fs = con_get_fullscreen_covering_ws(ws); | |
47 | return fs ? fs : con; | |
48 | } | |
49 | } | |
50 | ||
51 | /* Couldn't find leaf container, get a workspace. */ | |
52 | Output *output = get_output_containing(x, y); | |
53 | if (!output) { | |
54 | return NULL; | |
55 | } | |
56 | Con *content = output_get_content(output->con); | |
57 | /* Still descend because you can drag to the bar on an non-empty workspace. */ | |
58 | return con_descend_tiling_focused(content); | |
59 | } | |
60 | ||
61 | typedef enum { DT_SIBLING, | |
62 | DT_CENTER, | |
63 | DT_PARENT | |
64 | } drop_type_t; | |
65 | ||
66 | struct callback_params { | |
67 | xcb_window_t *indicator; | |
68 | Con **target; | |
69 | direction_t *direction; | |
70 | drop_type_t *drop_type; | |
71 | }; | |
72 | ||
73 | static Rect adjust_rect(Rect rect, direction_t direction, uint32_t threshold) { | |
74 | switch (direction) { | |
75 | case D_LEFT: | |
76 | rect.width = threshold; | |
77 | break; | |
78 | case D_UP: | |
79 | rect.height = threshold; | |
80 | break; | |
81 | case D_RIGHT: | |
82 | rect.x += (rect.width - threshold); | |
83 | rect.width = threshold; | |
84 | break; | |
85 | case D_DOWN: | |
86 | rect.y += (rect.height - threshold); | |
87 | rect.height = threshold; | |
88 | break; | |
89 | } | |
90 | return rect; | |
91 | } | |
92 | ||
93 | static bool con_on_side_of_parent(Con *con, direction_t direction) { | |
94 | const orientation_t orientation = orientation_from_direction(direction); | |
95 | direction_t reverse_direction; | |
96 | switch (direction) { | |
97 | case D_LEFT: | |
98 | reverse_direction = D_RIGHT; | |
99 | break; | |
100 | case D_RIGHT: | |
101 | reverse_direction = D_LEFT; | |
102 | break; | |
103 | case D_UP: | |
104 | reverse_direction = D_DOWN; | |
105 | break; | |
106 | case D_DOWN: | |
107 | reverse_direction = D_UP; | |
108 | break; | |
109 | } | |
110 | return (con_orientation(con->parent) != orientation || | |
111 | con->parent->layout == L_STACKED || con->parent->layout == L_TABBED || | |
112 | con_descend_direction(con->parent, reverse_direction) == con); | |
113 | } | |
114 | ||
115 | /* | |
116 | * The callback that is executed on every mouse move while dragging. On each | |
117 | * invocation we determine the drop target and the direction in which to insert | |
118 | * the dragged container. The indicator window is updated to show the new | |
119 | * position of the dragged container. The target container and direction are | |
120 | * passed out using the callback params. | |
121 | * | |
122 | */ | |
123 | DRAGGING_CB(drag_callback) { | |
124 | /* 30% of the container (minus the parent indicator) is used to drop the | |
125 | * dragged container as a sibling to the target */ | |
126 | const double sibling_indicator_percent_of_rect = 0.3; | |
127 | /* Use the base decoration height and add a few pixels. This makes the | |
128 | * outer indicator generally thin but at least thick enough to cover | |
129 | * container titles */ | |
130 | const double parent_indicator_max_size = render_deco_height() + logical_px(5); | |
131 | ||
132 | Con *target = find_drop_target(new_x, new_y); | |
133 | if (target == NULL) { | |
134 | return; | |
135 | } | |
136 | ||
137 | Rect rect = con_rect_plus_deco_height(target); | |
138 | ||
139 | direction_t direction = 0; | |
140 | drop_type_t drop_type = DT_CENTER; | |
141 | bool draw_window = true; | |
142 | const struct callback_params *params = extra; | |
143 | ||
144 | if (target->type == CT_WORKSPACE) { | |
145 | goto create_indicator; | |
146 | } | |
147 | ||
148 | /* Define the thresholds in pixels. The drop type depends on the cursor | |
149 | * position. */ | |
150 | const uint32_t min_rect_dimension = min(rect.width, rect.height); | |
151 | const uint32_t sibling_indicator_size = max(logical_px(2), (uint32_t)(sibling_indicator_percent_of_rect * min_rect_dimension)); | |
152 | const uint32_t parent_indicator_size = min( | |
153 | parent_indicator_max_size, | |
154 | /* For small containers, start where the sibling indicator finishes. | |
155 | * This is always at least 1 pixel. We use min() to not override the | |
156 | * sibling indicator: */ | |
157 | sibling_indicator_size - 1); | |
158 | ||
159 | /* Find which edge the cursor is closer to. */ | |
160 | const uint32_t d_left = new_x - rect.x; | |
161 | const uint32_t d_top = new_y - rect.y; | |
162 | const uint32_t d_right = rect.x + rect.width - new_x; | |
163 | const uint32_t d_bottom = rect.y + rect.height - new_y; | |
164 | const uint32_t d_min = min(min(d_left, d_right), min(d_top, d_bottom)); | |
165 | /* And move the container towards that direction. */ | |
166 | if (d_left == d_min) { | |
167 | direction = D_LEFT; | |
168 | } else if (d_top == d_min) { | |
169 | direction = D_UP; | |
170 | } else if (d_right == d_min) { | |
171 | direction = D_RIGHT; | |
172 | } else if (d_bottom == d_min) { | |
173 | direction = D_DOWN; | |
174 | } else { | |
175 | /* Keep the compiler happy */ | |
176 | ELOG("min() is broken\n"); | |
177 | assert(false); | |
178 | } | |
179 | const bool target_parent = (d_min < parent_indicator_size && | |
180 | con_on_side_of_parent(target, direction)); | |
181 | const bool target_sibling = (d_min < sibling_indicator_size); | |
182 | drop_type = target_parent ? DT_PARENT : (target_sibling ? DT_SIBLING : DT_CENTER); | |
183 | ||
184 | /* target == con makes sense only when we are moving away from target's parent. */ | |
185 | if (drop_type != DT_PARENT && target == con) { | |
186 | draw_window = false; | |
187 | xcb_destroy_window(conn, *(params->indicator)); | |
188 | *(params->indicator) = 0; | |
189 | goto create_indicator; | |
190 | } | |
191 | ||
192 | switch (drop_type) { | |
193 | case DT_PARENT: | |
194 | while (target->parent->type != CT_WORKSPACE && con_on_side_of_parent(target->parent, direction)) { | |
195 | target = target->parent; | |
196 | } | |
197 | rect = adjust_rect(target->parent->rect, direction, parent_indicator_size); | |
198 | break; | |
199 | case DT_CENTER: | |
200 | rect = target->rect; | |
201 | rect.x += sibling_indicator_size; | |
202 | rect.y += sibling_indicator_size; | |
203 | rect.width -= sibling_indicator_size * 2; | |
204 | rect.height -= sibling_indicator_size * 2; | |
205 | break; | |
206 | case DT_SIBLING: | |
207 | rect = adjust_rect(target->rect, direction, sibling_indicator_size); | |
208 | break; | |
209 | } | |
210 | ||
211 | create_indicator: | |
212 | if (draw_window) { | |
213 | if (*(params->indicator) == 0) { | |
214 | *(params->indicator) = create_drop_indicator(rect); | |
215 | } else { | |
216 | const uint32_t values[4] = {rect.x, rect.y, rect.width, rect.height}; | |
217 | const uint32_t mask = XCB_CONFIG_WINDOW_X | | |
218 | XCB_CONFIG_WINDOW_Y | | |
219 | XCB_CONFIG_WINDOW_WIDTH | | |
220 | XCB_CONFIG_WINDOW_HEIGHT; | |
221 | xcb_configure_window(conn, *(params->indicator), mask, values); | |
222 | } | |
223 | } | |
224 | x_mask_event_mask(~XCB_EVENT_MASK_ENTER_WINDOW); | |
225 | xcb_flush(conn); | |
226 | ||
227 | *(params->target) = target; | |
228 | *(params->direction) = direction; | |
229 | *(params->drop_type) = drop_type; | |
230 | } | |
231 | ||
232 | /* | |
233 | * Returns a new drop indicator window with the given initial coordinates. | |
234 | * | |
235 | */ | |
236 | static xcb_window_t create_drop_indicator(Rect rect) { | |
237 | uint32_t mask = 0; | |
238 | uint32_t values[2]; | |
239 | ||
240 | mask = XCB_CW_BACK_PIXEL; | |
241 | values[0] = config.client.focused.indicator.colorpixel; | |
242 | ||
243 | mask |= XCB_CW_OVERRIDE_REDIRECT; | |
244 | values[1] = 1; | |
245 | ||
246 | xcb_window_t indicator = create_window(conn, rect, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, | |
247 | XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_MOVE, false, mask, values); | |
248 | /* Change the window class to "i3-drag", so that it can be matched in a | |
249 | * compositor configuration. Note that the class needs to be changed before | |
250 | * mapping the window. */ | |
251 | xcb_change_property(conn, | |
252 | XCB_PROP_MODE_REPLACE, | |
253 | indicator, | |
254 | XCB_ATOM_WM_CLASS, | |
255 | XCB_ATOM_STRING, | |
256 | 8, | |
257 | (strlen("i3-drag") + 1) * 2, | |
258 | "i3-drag\0i3-drag\0"); | |
259 | xcb_map_window(conn, indicator); | |
260 | xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, indicator); | |
261 | ||
262 | return indicator; | |
263 | } | |
264 | ||
265 | /* | |
266 | * Initiates a mouse drag operation on a tiled window. | |
267 | * | |
268 | */ | |
269 | void tiling_drag(Con *con, xcb_button_press_event_t *event) { | |
270 | DLOG("Start dragging tiled container: con = %p\n", con); | |
271 | bool set_focus = (con == focused); | |
272 | bool set_fs = con->fullscreen_mode != CF_NONE; | |
273 | ||
274 | /* Don't change focus while dragging. */ | |
275 | x_mask_event_mask(~XCB_EVENT_MASK_ENTER_WINDOW); | |
276 | xcb_flush(conn); | |
277 | ||
278 | /* Indicate drop location while dragging. This blocks until the drag is completed. */ | |
279 | Con *target = NULL; | |
280 | direction_t direction; | |
281 | drop_type_t drop_type; | |
282 | xcb_window_t indicator = 0; | |
283 | const struct callback_params params = {&indicator, &target, &direction, &drop_type}; | |
284 | ||
285 | drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP, XCURSOR_CURSOR_MOVE, drag_callback, ¶ms); | |
286 | ||
287 | /* Dragging is done. We don't need the indicator window any more. */ | |
288 | xcb_destroy_window(conn, indicator); | |
289 | ||
290 | if (drag_result == DRAG_REVERT || | |
291 | target == NULL || | |
292 | (target == con && drop_type != DT_PARENT) || | |
293 | !con_exists(target)) { | |
294 | DLOG("drop aborted\n"); | |
295 | return; | |
296 | } | |
297 | ||
298 | const orientation_t orientation = orientation_from_direction(direction); | |
299 | const position_t position = position_from_direction(direction); | |
300 | const layout_t layout = orientation == VERT ? L_SPLITV : L_SPLITH; | |
301 | con_disable_fullscreen(con); | |
302 | switch (drop_type) { | |
303 | case DT_CENTER: | |
304 | /* Also handles workspaces.*/ | |
305 | DLOG("drop to center of %p\n", target); | |
306 | con_move_to_target(con, target); | |
307 | break; | |
308 | case DT_SIBLING: | |
309 | DLOG("drop %s %p\n", position_to_string(position), target); | |
310 | if (con_orientation(target->parent) != orientation) { | |
311 | /* If con and target are the only children of the same parent, we can just change | |
312 | * the parent's layout manually and then move con to the correct position. | |
313 | * tree_split checks for a parent with only one child so it would create a new | |
314 | * parent with the new layout. */ | |
315 | if (con->parent == target->parent && con_num_children(target->parent) == 2) { | |
316 | target->parent->layout = layout; | |
317 | } else { | |
318 | tree_split(target, orientation); | |
319 | } | |
320 | } | |
321 | ||
322 | insert_con_into(con, target, position); | |
323 | ||
324 | ipc_send_window_event("move", con); | |
325 | break; | |
326 | case DT_PARENT: { | |
327 | const bool parent_tabbed_or_stacked = (target->parent->layout == L_TABBED || target->parent->layout == L_STACKED); | |
328 | DLOG("drop %s (%s) of %s%p\n", | |
329 | direction_to_string(direction), | |
330 | position_to_string(position), | |
331 | parent_tabbed_or_stacked ? "tabbed/stacked " : "", | |
332 | target); | |
333 | if (parent_tabbed_or_stacked) { | |
334 | /* When dealing with tabbed/stacked the target can be in the | |
335 | * middle of the container. Thus, after a directional move, con | |
336 | * will still be bound to the tabbed/stacked parent. */ | |
337 | if (position == BEFORE) { | |
338 | target = TAILQ_FIRST(&(target->parent->nodes_head)); | |
339 | } else { | |
340 | target = TAILQ_LAST(&(target->parent->nodes_head), nodes_head); | |
341 | } | |
342 | } | |
343 | if (con != target) { | |
344 | insert_con_into(con, target, position); | |
345 | } | |
346 | /* tree_move can change the focus */ | |
347 | Con *old_focus = focused; | |
348 | tree_move(con, direction); | |
349 | if (focused != old_focus) { | |
350 | con_activate(old_focus); | |
351 | } | |
352 | break; | |
353 | } | |
354 | } | |
355 | /* Warning: target might not exist anymore */ | |
356 | target = NULL; | |
357 | ||
358 | /* Manage fullscreen status. */ | |
359 | if (set_focus || set_fs) { | |
360 | Con *fs = con_get_fullscreen_covering_ws(con_get_workspace(con)); | |
361 | if (fs == con) { | |
362 | ELOG("dragged container somehow got fullscreen again.\n"); | |
363 | assert(false); | |
364 | } else if (fs && set_focus && set_fs) { | |
365 | /* con was focused & fullscreen, disable other fullscreen container. */ | |
366 | con_disable_fullscreen(fs); | |
367 | } else if (fs) { | |
368 | /* con was not focused, prefer other fullscreen container. */ | |
369 | set_fs = set_focus = false; | |
370 | } else if (!set_focus) { | |
371 | /* con was not focused. If it was fullscreen and we are moving it to the focused | |
372 | * workspace we must focus it. */ | |
373 | set_focus = (set_fs && con_get_workspace(focused) == con_get_workspace(con)); | |
374 | } | |
375 | } | |
376 | if (set_fs) { | |
377 | con_enable_fullscreen(con, CF_OUTPUT); | |
378 | } | |
379 | if (set_focus) { | |
380 | workspace_show(con_get_workspace(con)); | |
381 | con_focus(con); | |
382 | } | |
383 | tree_render(); | |
384 | } |
475 | 475 | return position == BEFORE ? D_UP : D_DOWN; |
476 | 476 | } |
477 | 477 | } |
478 | ||
479 | /* | |
480 | * Converts direction to a string representation. | |
481 | * | |
482 | */ | |
483 | const char *direction_to_string(direction_t direction) { | |
484 | switch (direction) { | |
485 | case D_LEFT: | |
486 | return "left"; | |
487 | case D_RIGHT: | |
488 | return "right"; | |
489 | case D_UP: | |
490 | return "up"; | |
491 | case D_DOWN: | |
492 | return "down"; | |
493 | } | |
494 | return "invalid"; | |
495 | } | |
496 | ||
497 | /* | |
498 | * Converts position to a string representation. | |
499 | * | |
500 | */ | |
501 | const char *position_to_string(position_t position) { | |
502 | switch (position) { | |
503 | case BEFORE: | |
504 | return "before"; | |
505 | case AFTER: | |
506 | return "after"; | |
507 | } | |
508 | return "invalid"; | |
509 | } |
414 | 414 | * it is still in use by popular widget toolkits such as GTK+ and Java AWT. |
415 | 415 | * |
416 | 416 | */ |
417 | void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style) { | |
418 | /* This implementation simply mirrors Gnome's Metacity. Official | |
419 | * documentation of this hint is nowhere to be found. | |
420 | * For more information see: | |
421 | * https://people.gnome.org/~tthurman/docs/metacity/xprops_8h-source.html | |
422 | * https://stackoverflow.com/questions/13787553/detect-if-a-x11-window-has-decorations | |
417 | bool window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style) { | |
418 | /* See `man VendorShell' for more info, `XmNmwmDecorations' section: | |
419 | * https://linux.die.net/man/3/vendorshell | |
420 | * The following constants are adapted from <Xm/MwmUtil.h>. | |
423 | 421 | */ |
424 | 422 | #define MWM_HINTS_FLAGS_FIELD 0 |
425 | 423 | #define MWM_HINTS_DECORATIONS_FIELD 2 |
434 | 432 | |
435 | 433 | if (prop == NULL || xcb_get_property_value_length(prop) == 0) { |
436 | 434 | FREE(prop); |
437 | return; | |
435 | return false; | |
438 | 436 | } |
439 | 437 | |
440 | 438 | /* The property consists of an array of 5 uint32_t's. The first value is a |
460 | 458 | } |
461 | 459 | |
462 | 460 | FREE(prop); |
461 | return true; | |
463 | 462 | |
464 | 463 | #undef MWM_HINTS_FLAGS_FIELD |
465 | 464 | #undef MWM_HINTS_DECORATIONS_FIELD |
298 | 298 | |
299 | 299 | /* |
300 | 300 | * Returns true if the workspace is currently visible. Especially important for |
301 | * multi-monitor environments, as they can have multiple currenlty active | |
301 | * multi-monitor environments, as they can have multiple currently active | |
302 | 302 | * workspaces. |
303 | 303 | * |
304 | 304 | */ |
305 | 305 | bool workspace_is_visible(Con *ws) { |
306 | 306 | Con *output = con_get_output(ws); |
307 | if (output == NULL) | |
307 | if (output == NULL) { | |
308 | 308 | return false; |
309 | } | |
309 | 310 | Con *fs = con_get_fullscreen_con(output, CF_OUTPUT); |
310 | LOG("workspace visible? fs = %p, ws = %p\n", fs, ws); | |
311 | 311 | return (fs == ws); |
312 | 312 | } |
313 | 313 |
489 | 489 | struct deco_render_params *p = scalloc(1, sizeof(struct deco_render_params)); |
490 | 490 | |
491 | 491 | /* find out which colors to use */ |
492 | if (con->urgent) | |
492 | if (con->urgent) { | |
493 | 493 | p->color = &config.client.urgent; |
494 | else if (con == focused || con_inside_focused(con)) | |
494 | } else if (con == focused || con_inside_focused(con)) { | |
495 | 495 | p->color = &config.client.focused; |
496 | else if (con == TAILQ_FIRST(&(parent->focus_head))) | |
497 | p->color = &config.client.focused_inactive; | |
498 | else | |
496 | } else if (con == TAILQ_FIRST(&(parent->focus_head))) { | |
497 | if (config.client.got_focused_tab_title && !leaf && con_descend_focused(con) == focused) { | |
498 | /* Stacked/tabbed parent of focused container */ | |
499 | p->color = &config.client.focused_tab_title; | |
500 | } else { | |
501 | p->color = &config.client.focused_inactive; | |
502 | } | |
503 | } else { | |
499 | 504 | p->color = &config.client.unfocused; |
505 | } | |
500 | 506 | |
501 | 507 | p->border_style = con_border_style(con); |
502 | 508 | |
874 | 880 | con_state *state; |
875 | 881 | Rect rect = con->rect; |
876 | 882 | |
877 | //DLOG("Pushing changes for node %p / %s\n", con, con->name); | |
878 | 883 | state = state_for_frame(con->frame.id); |
879 | 884 | |
880 | 885 | if (state->name != NULL) { |
987 | 992 | win_depth = con->window->depth; |
988 | 993 | |
989 | 994 | /* Ensure we have valid dimensions for our surface. */ |
990 | // TODO This is probably a bug in the condition above as we should never enter this path | |
991 | // for height == 0. Also, we should probably handle width == 0 the same way. | |
995 | /* TODO: This is probably a bug in the condition above as we should | |
996 | * never enter this path for height == 0. Also, we should probably | |
997 | * handle width == 0 the same way. */ | |
992 | 998 | int width = MAX((int32_t)rect.width, 1); |
993 | 999 | int height = MAX((int32_t)rect.height, 1); |
994 | 1000 | |
995 | 1001 | xcb_create_pixmap(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height); |
996 | 1002 | draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id, |
997 | 1003 | get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height); |
1004 | draw_util_clear_surface(&(con->frame_buffer), (color_t){.red = 0.0, .green = 0.0, .blue = 0.0}); | |
998 | 1005 | |
999 | 1006 | /* For the graphics context, we disable GraphicsExposure events. |
1000 | 1007 | * Those will be sent when a CopyArea request cannot be fulfilled |
1007 | 1014 | con->pixmap_recreated = true; |
1008 | 1015 | |
1009 | 1016 | /* Don’t render the decoration for windows inside a stack which are |
1010 | * not visible right now */ | |
1011 | // TODO Should this work the same way for L_TABBED? | |
1017 | * not visible right now | |
1018 | * TODO: Should this work the same way for L_TABBED? */ | |
1012 | 1019 | if (!con->parent || |
1013 | 1020 | con->parent->layout != L_STACKED || |
1014 | 1021 | TAILQ_FIRST(&(con->parent->focus_head)) == con) |
1119 | 1126 | Con *current; |
1120 | 1127 | con_state *state; |
1121 | 1128 | |
1122 | //DLOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name); | |
1123 | 1129 | state = state_for_frame(con->frame.id); |
1124 | 1130 | |
1125 | 1131 | /* map/unmap if map state changed, also ensure that the child window |
1195 | 1201 | } |
1196 | 1202 | |
1197 | 1203 | DLOG("-- PUSHING WINDOW STACK --\n"); |
1198 | //DLOG("Disabling EnterNotify\n"); | |
1199 | 1204 | /* We need to keep SubstructureRedirect around, otherwise clients can send |
1200 | 1205 | * ConfigureWindow requests and get them applied directly instead of having |
1201 | 1206 | * them become ConfigureRequests that i3 handles. */ |
1204 | 1209 | if (state->mapped) |
1205 | 1210 | xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); |
1206 | 1211 | } |
1207 | //DLOG("Done, EnterNotify disabled\n"); | |
1208 | 1212 | bool order_changed = false; |
1209 | 1213 | bool stacking_changed = false; |
1210 | 1214 | |
1234 | 1238 | if (con_has_managed_window(state->con)) |
1235 | 1239 | memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t)); |
1236 | 1240 | |
1237 | //DLOG("stack: 0x%08x\n", state->id); | |
1238 | 1241 | con_state *prev = CIRCLEQ_PREV(state, state); |
1239 | 1242 | con_state *old_prev = CIRCLEQ_PREV(state, old_state); |
1240 | 1243 | if (prev != old_prev) |
1241 | 1244 | order_changed = true; |
1242 | 1245 | if ((state->initial || order_changed) && prev != CIRCLEQ_END(&state_head)) { |
1243 | 1246 | stacking_changed = true; |
1244 | //DLOG("Stacking 0x%08x above 0x%08x\n", prev->id, state->id); | |
1245 | 1247 | uint32_t mask = 0; |
1246 | 1248 | mask |= XCB_CONFIG_WINDOW_SIBLING; |
1247 | 1249 | mask |= XCB_CONFIG_WINDOW_STACK_MODE; |
1294 | 1296 | warp_to = NULL; |
1295 | 1297 | } |
1296 | 1298 | |
1297 | //DLOG("Re-enabling EnterNotify\n"); | |
1298 | 1299 | values[0] = FRAME_EVENT_MASK; |
1299 | 1300 | CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { |
1300 | 1301 | if (state->mapped) |
1301 | 1302 | xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); |
1302 | 1303 | } |
1303 | //DLOG("Done, EnterNotify re-enabled\n"); | |
1304 | 1304 | |
1305 | 1305 | x_deco_recurse(con); |
1306 | 1306 | |
1386 | 1386 | CIRCLEQ_REMOVE(&old_state_head, state, old_state); |
1387 | 1387 | CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state); |
1388 | 1388 | } |
1389 | //CIRCLEQ_FOREACH(state, &old_state_head, old_state) { | |
1390 | // DLOG("old stack: 0x%08x\n", state->id); | |
1391 | //} | |
1392 | 1389 | |
1393 | 1390 | xcb_flush(conn); |
1394 | 1391 | } |
1401 | 1398 | void x_raise_con(Con *con) { |
1402 | 1399 | con_state *state; |
1403 | 1400 | state = state_for_frame(con->frame.id); |
1404 | //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id); | |
1405 | 1401 | |
1406 | 1402 | CIRCLEQ_REMOVE(&state_head, state, state); |
1407 | 1403 | CIRCLEQ_INSERT_HEAD(&state_head, state, state); |
432 | 432 | bound = true; |
433 | 433 | /* Let the user know bind() was successful, so that they know the |
434 | 434 | * error messages can be disregarded. */ |
435 | fprintf(stderr, "Successfuly bound to %s\n", addr.sun_path); | |
435 | fprintf(stderr, "Successfully bound to %s\n", addr.sun_path); | |
436 | 436 | sun_path = sstrdup(addr.sun_path); |
437 | 437 | break; |
438 | 438 | } |
41 | 41 | exit_forcefully |
42 | 42 | workspace_exists |
43 | 43 | focused_ws |
44 | focused_output | |
44 | 45 | get_socket_path |
45 | 46 | launch_with_config |
46 | 47 | get_i3_log |
663 | 664 | (scalar grep { $_ eq $name } @{get_workspace_names()}) > 0; |
664 | 665 | } |
665 | 666 | |
666 | =head2 focused_ws | |
667 | ||
668 | Returns the name of the currently focused workspace. | |
669 | ||
670 | my $ws = focused_ws; | |
671 | is($ws, '1', 'i3 starts on workspace 1'); | |
672 | ||
673 | =cut | |
674 | sub focused_ws { | |
667 | =head2 focused_output | |
668 | ||
669 | Returns the name of the currently focused output. | |
670 | ||
671 | is(focused_output, 'fake-0', 'i3 starts on output 0'); | |
672 | ||
673 | =cut | |
674 | sub _focused_output { | |
675 | 675 | my $i3 = i3(get_socket_path()); |
676 | 676 | my $tree = $i3->get_tree->recv; |
677 | 677 | my $focused = $tree->{focus}->[0]; |
678 | 678 | my $output = first { $_->{id} == $focused } @{$tree->{nodes}}; |
679 | return $output; | |
680 | } | |
681 | ||
682 | sub focused_output { | |
683 | return _focused_output->{name} | |
684 | } | |
685 | ||
686 | =head2 focused_ws | |
687 | ||
688 | Returns the name of the currently focused workspace. | |
689 | ||
690 | my $ws = focused_ws; | |
691 | is($ws, '1', 'i3 starts on workspace 1'); | |
692 | ||
693 | =cut | |
694 | ||
695 | sub focused_ws { | |
696 | my $output = _focused_output; | |
679 | 697 | my $content = first { $_->{type} eq 'con' } @{$output->{nodes}}; |
680 | 698 | my $first = first { $_->{fullscreen_mode} == 1 } @{$content->{nodes}}; |
681 | 699 | return $first->{name} |
779 | 797 | my ($pid, $socketpath) = @_; |
780 | 798 | $socketpath ||= get_socket_path(); |
781 | 799 | |
800 | $SIG{CHLD} = undef; | |
801 | ||
782 | 802 | my $exited = 0; |
783 | 803 | eval { |
784 | 804 | say "Exiting i3 cleanly..."; |
816 | 836 | sub exit_forcefully { |
817 | 837 | my ($pid, $signal) = @_; |
818 | 838 | $signal ||= 'TERM'; |
839 | ||
840 | $SIG{CHLD} = undef; | |
819 | 841 | |
820 | 842 | # Send the given signal to the i3 instance and wait for up to 10s |
821 | 843 | # for it to terminate. |
940 | 962 | return ${^CHILD_ERROR_NATIVE}; |
941 | 963 | } |
942 | 964 | |
965 | $SIG{CHLD} = sub { | |
966 | # don't change $! and $? outside handler | |
967 | local ($!, $?); | |
968 | ||
969 | my $child = waitpid -1, POSIX::WNOHANG; | |
970 | warn "SIGCHLD, waitpid() = $child"; | |
971 | if ($child == $i3_pid) { | |
972 | warn "i3 died, exiting!"; | |
973 | exit 1; | |
974 | } | |
975 | }; | |
976 | ||
943 | 977 | # force update of the cached socket path in lib/i3test |
944 | 978 | # as soon as i3 has started |
945 | 979 | $cv->cb(sub { get_socket_path(0) }); |
971 | 1005 | # Sync in case not all windows are managed by i3 just yet. |
972 | 1006 | sync_with_i3; |
973 | 1007 | cmd '[title=".*"] kill'; |
1008 | # Sync to make sure x_window_kill() calls have taken effect. | |
1009 | sync_with_i3; | |
974 | 1010 | } |
975 | 1011 | |
976 | 1012 | =head2 events_for($subscribecb, [ $rettype ], [ $eventcbs ]) |
13 | 13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf |
14 | 14 | # (unless you are already familiar with Perl) |
15 | 15 | # |
16 | # Tests whether we can switch to a non-existant workspace | |
16 | # Tests whether we can switch to a non-existent workspace | |
17 | 17 | # (necessary for further tests) |
18 | 18 | # |
19 | 19 | use List::Util qw(first); |
266 | 266 | like($output->[3], qr|^bindsym Mod1\+f resize grow down 23 px$|, 'resize bottom changed'); |
267 | 267 | |
268 | 268 | ##################################################################### |
269 | # also resizing, but with indention this time | |
269 | # also resizing, but with indentation this time | |
270 | 270 | ##################################################################### |
271 | 271 | |
272 | 272 | like($output->[4], qr|^bindsym Mod1\+f resize grow left 10 px$|, 'resize left changed'); |
475 | 475 | ################################################################################ |
476 | 476 | |
477 | 477 | $config = <<'EOT'; |
478 | client.focused #4c7899 #285577 #ffffff #2e9ef4 #b34d4c | |
479 | client.focused_inactive #333333 #5f676a #ffffff #484e50 | |
480 | client.unfocused #333333 #222222 #888888 #292d2e | |
481 | client.urgent #2f343a #900000 #ffffff #900000 #c00000 | |
482 | client.placeholder #000000 #0c0c0c #ffffff #000000 | |
478 | client.focused #4c7899 #285577 #ffffff #2e9ef4 #b34d4c | |
479 | client.focused_inactive #333333 #5f676a #ffffff #484e50 | |
480 | client.focused_tab_title #444444 #555555 #ffffff | |
481 | client.unfocused #333333 #222222 #888888 #292d2e | |
482 | client.urgent #2f343a #900000 #ffffff #900000 #c00000 | |
483 | client.placeholder #000000 #0c0c0c #ffffff #000000 | |
483 | 484 | EOT |
484 | 485 | |
485 | 486 | $expected = <<'EOT'; |
486 | 487 | cfg_color(client.focused, #4c7899, #285577, #ffffff, #2e9ef4, #b34d4c) |
487 | 488 | cfg_color(client.focused_inactive, #333333, #5f676a, #ffffff, #484e50, NULL) |
489 | cfg_color(client.focused_tab_title, #444444, #555555, #ffffff, NULL, NULL) | |
488 | 490 | cfg_color(client.unfocused, #333333, #222222, #888888, #292d2e, NULL) |
489 | 491 | cfg_color(client.urgent, #2f343a, #900000, #ffffff, #900000, #c00000) |
490 | 492 | cfg_color(client.placeholder, #000000, #0c0c0c, #ffffff, #000000, NULL) |
550 | 552 | exec |
551 | 553 | client.background |
552 | 554 | client.focused_inactive |
555 | client.focused_tab_title | |
553 | 556 | client.focused |
554 | 557 | client.unfocused |
555 | 558 | client.urgent |
70 | 70 | cmd 'mark left'; |
71 | 71 | |
72 | 72 | # |
73 | # get_marks replys an array of marks, whose order is undefined, | |
73 | # get_marks replies an array of marks, whose order is undefined, | |
74 | 74 | # so we use sort to be able to compare the output |
75 | 75 | # |
76 | 76 |
17 | 17 | # i3 config file (v4) |
18 | 18 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 |
19 | 19 | |
20 | # fake-1 under fake-0 to not interfere with left/right wraping | |
20 | # fake-1 under fake-0 to not interfere with left/right wrapping | |
21 | 21 | fake-outputs 1024x768+0+0,1024x768+0+1024 |
22 | 22 | workspace X output fake-1 |
23 | 23 | EOT |
55 | 55 | my $focus = AnyEvent->condvar; |
56 | 56 | |
57 | 57 | my @events = events_for( |
58 | sub { cmd $cmd }, | |
58 | sub { | |
59 | cmd $cmd; | |
60 | # Sync to make sure x_window_kill() calls have taken effect. | |
61 | sync_with_i3; | |
62 | }, | |
59 | 63 | 'window'); |
60 | 64 | |
61 | 65 | is(scalar @events, 1, 'Received 1 event'); |
61 | 61 | font \ |
62 | 62 | -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 |
63 | 63 | |
64 | # Use line contiuation with too many lines (>4096 characters). | |
64 | # Use line continuation with too many lines (>4096 characters). | |
65 | 65 | # This config is invalid. Use it to ensure no buffer overflow. |
66 | 66 | bindsym Mod1+b \ |
67 | 67 | 0001-This is a very very very very very very very very very very very very very very very very very long cmd \ |
20 | 20 | # |
21 | 21 | # Ticket: #2318 |
22 | 22 | # Bug still in: 4.12-46-g2123888 |
23 | use i3test; | |
23 | use i3test i3_autostart => 0; | |
24 | ||
25 | my $pid = launch_with_config('-default'); | |
24 | 26 | |
25 | 27 | # We cannot use events_for in this test as we cannot send events after |
26 | 28 | # issuing the restart/shutdown command. |
58 | 60 | } |
59 | 61 | })->recv; |
60 | 62 | |
61 | cmd 'exit'; | |
63 | exit_gracefully($pid); | |
62 | 64 | |
63 | 65 | $e = $cv->recv; |
64 | 66 |
13 | 13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf |
14 | 14 | # (unless you are already familiar with Perl) |
15 | 15 | # |
16 | # Verify that the corrent focus stack order is preserved after various | |
16 | # Verify that the current focus stack order is preserved after various | |
17 | 17 | # operations. |
18 | 18 | use i3test i3_config => <<EOT; |
19 | 19 | # i3 config file (v4) |
42 | 42 | * | C | |
43 | 43 | * +-------+ |
44 | 44 | * |
45 | * - Zone A is completly opaque. | |
45 | * - Zone A is completely opaque. | |
46 | 46 | * - Zone B is clickable through (input shape). |
47 | * - Zone C is completly transparent (bounding shape). | |
47 | * - Zone C is completely transparent (bounding shape). | |
48 | 48 | */ |
49 | 49 | void set_shape(long window_id) { |
50 | 50 | xcb_rectangle_t bounding_rectangle = { 0, 0, 100, 50 }; |
21 | 21 | # i3 config file (v4) |
22 | 22 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 |
23 | 23 | |
24 | # fake-1 under fake-0 to not interfere with left/right wraping | |
24 | # fake-1 under fake-0 to not interfere with left/right wrapping | |
25 | 25 | fake-outputs 1024x768+0+0,1024x768+0+1024 |
26 | 26 | workspace X output fake-1 |
27 | 27 | EOT |
37 | 37 | cmd 'title_window_icon on'; |
38 | 38 | isnt(window_icon_padding($tmp), -1, 'window_icon_padding no longer -1'); |
39 | 39 | |
40 | cmd 'title_window_icon toggle'; | |
41 | is(window_icon_padding($tmp), -1, 'window_icon_padding back to -1'); | |
42 | ||
43 | cmd 'title_window_icon toggle'; | |
44 | isnt(window_icon_padding($tmp), -1, 'window_icon_padding no longer -1 again'); | |
45 | ||
46 | cmd 'title_window_icon off'; | |
47 | is(window_icon_padding($tmp), -1, 'window_icon_padding back to -1'); | |
48 | ||
49 | cmd 'title_window_icon padding 3px'; | |
50 | is(window_icon_padding($tmp), 3, 'window_icon_padding set to 3'); | |
51 | ||
52 | cmd 'title_window_icon toggle'; | |
53 | ok(window_icon_padding($tmp) < 0, 'window_icon_padding toggled off'); | |
54 | ||
55 | cmd 'title_window_icon toggle'; | |
56 | is(window_icon_padding($tmp), 3, 'window_icon_padding toggled back to 3'); | |
57 | ||
58 | cmd 'title_window_icon toggle 5px'; | |
59 | ok(window_icon_padding($tmp) < 0, 'window_icon_padding toggled off'); | |
60 | ||
61 | cmd 'title_window_icon toggle 5px'; | |
62 | is(window_icon_padding($tmp), 5, 'window_icon_padding toggled on to 5px'); | |
63 | ||
64 | cmd 'title_window_icon toggle 5px'; | |
65 | ok(window_icon_padding($tmp) < 0, 'window_icon_padding toggled off'); | |
66 | ||
67 | cmd 'title_window_icon toggle 4px'; | |
68 | is(window_icon_padding($tmp), 4, 'window_icon_padding toggled on to 4px'); | |
69 | ||
40 | 70 | exit_gracefully($pid); |
41 | 71 | |
42 | 72 | ################################################################################ |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test that commands with more than 10 non-identified words doesn't works | |
17 | # 10 is the magic number chosen for the stack size which is why it's used here | |
18 | # Ticket: #2968 | |
19 | # Bug still in: 4.19.2-103-gfc65ca36 | |
20 | use i3test; | |
21 | ||
22 | ###################################################################### | |
23 | # 1) run a long command | |
24 | ###################################################################### | |
25 | ||
26 | my $i3 = i3(get_socket_path()); | |
27 | my $tmp = fresh_workspace; | |
28 | ||
29 | my $floatwin = open_floating_window; | |
30 | ||
31 | ||
32 | my ($absolute_before, $top_before) = $floatwin->rect; | |
33 | ||
34 | cmd 'move window container to window container to window container to left'; | |
35 | ||
36 | sync_with_i3; | |
37 | ||
38 | my ($absolute, $top) = $floatwin->rect; | |
39 | ||
40 | is($absolute->x, ($absolute_before->x - 10), 'moved 10 px to the left'); | |
41 | is($absolute->y, $absolute_before->y, 'y not changed'); | |
42 | is($absolute->width, $absolute_before->width, 'width not changed'); | |
43 | is($absolute->height, $absolute_before->height, 'height not changed'); | |
44 | ||
45 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test dragging containers. | |
17 | ||
18 | my ($width, $height) = (1000, 500); | |
19 | ||
20 | my $config = <<"EOT"; | |
21 | # i3 config file (v4) | |
22 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
23 | ||
24 | focus_follows_mouse no | |
25 | floating_modifier Mod1 | |
26 | ||
27 | # 2 side by side outputs | |
28 | fake-outputs ${width}x${height}+0+0P,${width}x${height}+${width}+0 | |
29 | ||
30 | bar { | |
31 | output primary | |
32 | } | |
33 | EOT | |
34 | use i3test i3_autostart => 0; | |
35 | use i3test::XTEST; | |
36 | my $pid = launch_with_config($config); | |
37 | ||
38 | sub start_drag { | |
39 | my ($pos_x, $pos_y) = @_; | |
40 | die "Drag outside of bounds!" unless $pos_x < $width * 2 && $pos_y < $height; | |
41 | ||
42 | $x->root->warp_pointer($pos_x, $pos_y); | |
43 | sync_with_i3; | |
44 | ||
45 | xtest_key_press(64); # Alt_L | |
46 | xtest_button_press(1, $pos_x, $pos_y); | |
47 | xtest_sync_with_i3; | |
48 | } | |
49 | ||
50 | sub end_drag { | |
51 | my ($pos_x, $pos_y) = @_; | |
52 | die "Drag outside of bounds!" unless $pos_x < $width * 2 && $pos_y < $height; | |
53 | ||
54 | $x->root->warp_pointer($pos_x, $pos_y); | |
55 | sync_with_i3; | |
56 | ||
57 | xtest_button_release(1, $pos_x, $pos_y); | |
58 | xtest_key_release(64); # Alt_L | |
59 | xtest_sync_with_i3; | |
60 | } | |
61 | ||
62 | my ($ws1, $ws2); | |
63 | my ($A, $B, $tmp); | |
64 | my ($A_id, $B_id); | |
65 | ||
66 | sub move_subtest { | |
67 | my ($cb, $win) = @_; | |
68 | ||
69 | my @events = events_for($cb, 'window'); | |
70 | my @move = grep { $_->{change} eq 'move' } @events; | |
71 | ||
72 | is(scalar @move, 1, 'Received 1 window::move event'); | |
73 | is($move[0]->{container}->{window}, $A->{id}, "window id matches"); | |
74 | } | |
75 | ||
76 | ############################################################################### | |
77 | # Drag floating container onto an empty workspace. | |
78 | ############################################################################### | |
79 | ||
80 | $ws2 = fresh_workspace(output => 1); | |
81 | $ws1 = fresh_workspace(output => 0); | |
82 | $A = open_floating_window(rect => [ 30, 30, 50, 50 ]); | |
83 | ||
84 | start_drag(40, 40); | |
85 | end_drag(1050, 50); | |
86 | ||
87 | is($x->input_focus, $A->id, 'Floating window moved to the right workspace'); | |
88 | is($ws2, focused_ws, 'Empty workspace focused after floating window dragged to it'); | |
89 | ||
90 | ############################################################################### | |
91 | # Drag tiling container onto an empty workspace. | |
92 | ############################################################################### | |
93 | ||
94 | subtest "Draging tiling container onto an empty workspace produces move event", \&move_subtest, | |
95 | sub { | |
96 | ||
97 | $ws2 = fresh_workspace(output => 1); | |
98 | $ws1 = fresh_workspace(output => 0); | |
99 | $A = open_window; | |
100 | ||
101 | start_drag(50, 50); | |
102 | end_drag(1050, 50); | |
103 | ||
104 | is($x->input_focus, $A->id, 'Tiling window moved to the right workspace'); | |
105 | is($ws2, focused_ws, 'Empty workspace focused after tiling window dragged to it'); | |
106 | ||
107 | }; | |
108 | ||
109 | ############################################################################### | |
110 | # Drag tiling container onto a container that closes before the drag is | |
111 | # complete. | |
112 | ############################################################################### | |
113 | ||
114 | $ws1 = fresh_workspace(output => 0); | |
115 | $A = open_window; | |
116 | open_window; | |
117 | ||
118 | start_drag(600, 300); # Start dragging the second window. | |
119 | ||
120 | # Try to place it on the first window. | |
121 | $x->root->warp_pointer(50, 50); | |
122 | sync_with_i3; | |
123 | ||
124 | cmd '[id=' . $A->id . '] kill'; | |
125 | sync_with_i3; | |
126 | end_drag(50, 50); | |
127 | ||
128 | is(@{get_ws_content($ws1)}, 1, 'One container left in ws1'); | |
129 | ||
130 | ############################################################################### | |
131 | # Drag tiling container onto a tiling container on an other workspace. | |
132 | ############################################################################### | |
133 | ||
134 | subtest "Draging tiling container onto a tiling container on an other workspace produces move event", \&move_subtest, | |
135 | sub { | |
136 | ||
137 | $ws2 = fresh_workspace(output => 1); | |
138 | open_window; | |
139 | $B_id = get_focused($ws2); | |
140 | $ws1 = fresh_workspace(output => 0); | |
141 | $A = open_window; | |
142 | $A_id = get_focused($ws1); | |
143 | ||
144 | start_drag(50, 50); | |
145 | end_drag(1500, 250); # Center of right output, inner region. | |
146 | ||
147 | is($ws2, focused_ws, 'Workspace focused after tiling window dragged to it'); | |
148 | $ws2 = get_ws($ws2); | |
149 | is($ws2->{focus}[0], $A_id, 'A focused first, dragged container kept focus'); | |
150 | is($ws2->{focus}[1], $B_id, 'B focused second'); | |
151 | ||
152 | }; | |
153 | ||
154 | ############################################################################### | |
155 | # Drag tiling container onto a floating container on an other workspace. | |
156 | ############################################################################### | |
157 | ||
158 | subtest "Draging tiling container onto a floating container on an other workspace produces move event", \&move_subtest, | |
159 | sub { | |
160 | ||
161 | $ws2 = fresh_workspace(output => 1); | |
162 | open_floating_window; | |
163 | $B_id = get_focused($ws2); | |
164 | $ws1 = fresh_workspace(output => 0); | |
165 | $A = open_window; | |
166 | $A_id = get_focused($ws1); | |
167 | ||
168 | start_drag(50, 50); | |
169 | end_drag(1500, 250); | |
170 | ||
171 | is($ws2, focused_ws, 'Workspace with one floating container focused after tiling window dragged to it'); | |
172 | $ws2 = get_ws($ws2); | |
173 | is($ws2->{focus}[0], $A_id, 'A focused first, dragged container kept focus'); | |
174 | is($ws2->{floating_nodes}[0]->{nodes}[0]->{id}, $B_id, 'B exists & floating'); | |
175 | ||
176 | }; | |
177 | ||
178 | ############################################################################### | |
179 | # Drag tiling container onto a bar. | |
180 | ############################################################################### | |
181 | ||
182 | subtest "Draging tiling container onto a bar produces move event", \&move_subtest, | |
183 | sub { | |
184 | ||
185 | $ws1 = fresh_workspace(output => 0); | |
186 | open_window; | |
187 | $B_id = get_focused($ws1); | |
188 | $ws2 = fresh_workspace(output => 1); | |
189 | $A = open_window; | |
190 | $A_id = get_focused($ws2); | |
191 | ||
192 | start_drag(1500, 250); | |
193 | end_drag(1, 498); # Bar on bottom of left output. | |
194 | ||
195 | is($ws1, focused_ws, 'Workspace focused after tiling window dragged to its bar'); | |
196 | $ws1 = get_ws($ws1); | |
197 | is($ws1->{focus}[0], $A_id, 'B focused first, dragged container kept focus'); | |
198 | is($ws1->{focus}[1], $B_id, 'A focused second'); | |
199 | ||
200 | }; | |
201 | ||
202 | ############################################################################### | |
203 | # Drag an unfocused tiling container onto it's self. | |
204 | ############################################################################### | |
205 | ||
206 | $ws1 = fresh_workspace(output => 0); | |
207 | open_window; | |
208 | $A_id = get_focused($ws1); | |
209 | open_window; | |
210 | $B_id = get_focused($ws1); | |
211 | ||
212 | start_drag(50, 50); | |
213 | end_drag(450, 450); | |
214 | ||
215 | $ws1 = get_ws($ws1); | |
216 | is($ws1->{focus}[0], $B_id, 'B focused first, kept focus'); | |
217 | is($ws1->{focus}[1], $A_id, 'A focused second, unfocused dragged container didn\'t gain focus'); | |
218 | ||
219 | ############################################################################### | |
220 | # Drag an unfocused tiling container onto an occupied workspace. | |
221 | ############################################################################### | |
222 | ||
223 | subtest "Draging unfocused tiling container onto an occupied workspace produces move event", \&move_subtest, | |
224 | sub { | |
225 | ||
226 | $ws1 = fresh_workspace(output => 0); | |
227 | $A = open_window; | |
228 | $A_id = get_focused($ws1); | |
229 | $ws2 = fresh_workspace(output => 1); | |
230 | open_window; | |
231 | $B_id = get_focused($ws2); | |
232 | ||
233 | start_drag(50, 50); | |
234 | end_drag(1500, 250); # Center of right output, inner region. | |
235 | ||
236 | is($ws2, focused_ws, 'Workspace remained focused after dragging unfocused container'); | |
237 | $ws2 = get_ws($ws2); | |
238 | is($ws2->{focus}[0], $B_id, 'B focused first, kept focus'); | |
239 | is($ws2->{focus}[1], $A_id, 'A focused second, unfocused container didn\'t steal focus'); | |
240 | ||
241 | }; | |
242 | ||
243 | ############################################################################### | |
244 | # Drag fullscreen container onto window in same workspace. | |
245 | ############################################################################### | |
246 | ||
247 | $ws1 = fresh_workspace(output => 0); | |
248 | open_window; | |
249 | $A = open_window; | |
250 | cmd 'fullscreen enable'; | |
251 | ||
252 | start_drag(900, 100); # Second window | |
253 | end_drag(50, 50); # To first window | |
254 | ||
255 | is($ws1, focused_ws, 'Workspace remained focused after dragging fullscreen container'); | |
256 | is_num_fullscreen($ws1, 1, 'Container still fullscreened'); | |
257 | is($x->input_focus, $A->id, 'Fullscreen container still focused'); | |
258 | ||
259 | ############################################################################### | |
260 | # Drag unfocused fullscreen container onto window in other workspace. | |
261 | ############################################################################### | |
262 | ||
263 | subtest "Draging unfocused fullscreen container onto window in other workspace produces move event", \&move_subtest, | |
264 | sub { | |
265 | ||
266 | $ws1 = fresh_workspace(output => 0); | |
267 | $A = open_window; | |
268 | cmd 'fullscreen enable'; | |
269 | $ws2 = fresh_workspace(output => 1); | |
270 | open_window; | |
271 | open_window; | |
272 | ||
273 | start_drag(900, 100); | |
274 | end_drag(1000 + 500 * 0.15 + 10, 200); # left of leftmost window | |
275 | ||
276 | is($ws2, focused_ws, 'Workspace still focused after dragging fullscreen container to it'); | |
277 | is_num_fullscreen($ws1, 0, 'No fullscreen container in first workspace'); | |
278 | is_num_fullscreen($ws2, 1, 'Moved container still fullscreened'); | |
279 | is($x->input_focus, $A->id, 'Fullscreen container now focused'); | |
280 | $ws2 = get_ws($ws2); | |
281 | is($ws2->{nodes}->[0]->{window}, $A->id, 'Fullscreen container now leftmost window in second workspace'); | |
282 | ||
283 | }; | |
284 | ||
285 | ############################################################################### | |
286 | # Drag unfocused fullscreen container onto left outter region of window in | |
287 | # other workspace. The container shouldn't end up in $ws2 because it was | |
288 | # dragged onto the outter region of the leftmost window. We must also check | |
289 | # that the focus remains on the other window. | |
290 | ############################################################################### | |
291 | ||
292 | subtest "Draging unfocused fullscreen container onto left outter region of window in other workspace produces move event", \&move_subtest, | |
293 | sub { | |
294 | ||
295 | $ws1 = fresh_workspace(output => 0); | |
296 | open_window for (1..3); | |
297 | $A = open_window; | |
298 | $tmp = get_focused($ws1); | |
299 | cmd 'fullscreen enable'; | |
300 | $ws2 = fresh_workspace(output => 1); | |
301 | $B = open_window; | |
302 | ||
303 | start_drag(990, 100); # rightmost of $ws1 | |
304 | end_drag(1004, 100); # outter region of window of $ws2 | |
305 | ||
306 | is($ws2, focused_ws, 'Workspace still focused after dragging fullscreen container to it'); | |
307 | is_num_fullscreen($ws1, 1, 'Fullscreen container still in first workspace'); | |
308 | is_num_fullscreen($ws2, 0, 'No fullscreen container in second workspace'); | |
309 | is($x->input_focus, $B->id, 'Window of second workspace still has focus'); | |
310 | is(get_focused($ws1), $tmp, 'Fullscreen container still focused in first workspace'); | |
311 | $ws1 = get_ws($ws1); | |
312 | is($ws1->{nodes}->[3]->{window}, $A->id, 'Fullscreen container still rightmost window in first workspace'); | |
313 | ||
314 | }; | |
315 | ||
316 | exit_gracefully($pid); | |
317 | ||
318 | ############################################################################### | |
319 | ||
320 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test that i3 does not get stuck in an endless loop between two windows that | |
17 | # set transient_for for each other. | |
18 | # Ticket: #4404 | |
19 | # Bug still in: 4.20-69-g43e805a00 | |
20 | # | |
21 | use i3test i3_config => <<EOT; | |
22 | # i3 config file (v4) | |
23 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
24 | ||
25 | popup_during_fullscreen smart; | |
26 | EOT | |
27 | ||
28 | my $fs = open_window; | |
29 | cmd 'fullscreen enable'; | |
30 | ||
31 | my $w1 = open_window({ dont_map => 1 }); | |
32 | my $w2 = open_window({ dont_map => 1 }); | |
33 | ||
34 | $w1->transient_for($w2); | |
35 | $w2->transient_for($w1); | |
36 | $w1->map; | |
37 | $w2->map; | |
38 | ||
39 | does_i3_live; | |
40 | ||
41 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Verifies that bar config blocks get the i3-wide font configured, | |
17 | # regardless of where the font is configured in the config file | |
18 | # (before or after the bar config blocks). | |
19 | # Ticket: #5031 | |
20 | # Bug still in: 4.20-105-g4db383e4 | |
21 | use i3test i3_config => <<'EOT'; | |
22 | # i3 config file (v4) | |
23 | ||
24 | bar { | |
25 | # no font directive here, no i3-wide font configured (yet) | |
26 | } | |
27 | ||
28 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
29 | EOT | |
30 | ||
31 | my $i3 = i3(get_socket_path(0)); | |
32 | my $bars = $i3->get_bar_config()->recv; | |
33 | ||
34 | my $bar_id = shift @$bars; | |
35 | my $bar_config = $i3->get_bar_config($bar_id)->recv; | |
36 | is($bar_config->{font}, 'fixed', 'font ok'); | |
37 | ||
38 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Verifies that any trailing whitespace in strings (including in | |
17 | # “bar { output <output> }” in particular) is stripped. | |
18 | # Ticket: #5064 | |
19 | # Bug still in: 4.20-105-g4db383e4 | |
20 | use i3test i3_autostart => 0; | |
21 | ||
22 | # Test with a single output. | |
23 | ||
24 | my $config = <<EOT; | |
25 | # i3 config file (v4) | |
26 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
27 | ||
28 | bar { | |
29 | output anything | |
30 | } | |
31 | EOT | |
32 | ||
33 | my $pid = launch_with_config($config); | |
34 | ||
35 | my $i3 = i3(get_socket_path(0)); | |
36 | my $bars = $i3->get_bar_config()->recv; | |
37 | is(@$bars, 1, 'one bar configured'); | |
38 | my $bar_id = shift @$bars; | |
39 | ||
40 | my $bar_config = $i3->get_bar_config($bar_id)->recv; | |
41 | is_deeply($bar_config->{outputs}, [ 'anything' ], 'outputs do not have trailing whitespace'); | |
42 | ||
43 | exit_gracefully($pid); | |
44 | ||
45 | # Test with multiple outputs for a single bar. | |
46 | ||
47 | $config = <<EOT; | |
48 | # i3 config file (v4) | |
49 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
50 | ||
51 | bar { | |
52 | output nospace | |
53 | output singlespace | |
54 | } | |
55 | EOT | |
56 | ||
57 | $pid = launch_with_config($config); | |
58 | ||
59 | $i3 = i3(get_socket_path(0)); | |
60 | $bars = $i3->get_bar_config()->recv; | |
61 | is(@$bars, 1, 'one bar configured'); | |
62 | $bar_id = shift @$bars; | |
63 | ||
64 | $bar_config = $i3->get_bar_config($bar_id)->recv; | |
65 | is_deeply($bar_config->{outputs}, [ 'nospace', 'singlespace' ], 'outputs do not have trailing whitespace'); | |
66 | ||
67 | exit_gracefully($pid); | |
68 | ||
69 | ||
70 | done_testing; |
29 | 29 | ################################################################################ |
30 | 30 | # use 'focus output' and verify that focus gets changed appropriately |
31 | 31 | ################################################################################ |
32 | ||
33 | sub focused_output { | |
34 | my $tree = $i3->get_tree->recv; | |
35 | my $focused = $tree->{focus}->[0]; | |
36 | my $output = first { $_->{id} == $focused } @{$tree->{nodes}}; | |
37 | return $output->{name}; | |
38 | } | |
39 | 32 | |
40 | 33 | sync_with_i3; |
41 | 34 | $x->root->warp_pointer(0, 0); |
148 | 148 | # Ensure that focusing right/left works in the expected order. |
149 | 149 | ############################################################################ |
150 | 150 | |
151 | sub get_focused_output { | |
152 | my $tree = i3(get_socket_path())->get_tree->recv; | |
153 | my ($focused_id) = @{$tree->{focus}}; | |
154 | my ($output) = grep { $_->{id} == $focused_id } @{$tree->{nodes}}; | |
155 | return $output->{name}; | |
156 | } | |
157 | ||
158 | is(get_focused_output(), 'fake-0', 'focus on fake-0'); | |
151 | is(focused_output, 'fake-0', 'focus on fake-0'); | |
159 | 152 | |
160 | 153 | cmd 'focus output right'; |
161 | is(get_focused_output(), 'fake-1', 'focus on fake-1'); | |
154 | is(focused_output, 'fake-1', 'focus on fake-1'); | |
162 | 155 | |
163 | 156 | cmd 'focus output right'; |
164 | is(get_focused_output(), 'fake-2', 'focus on fake-2'); | |
157 | is(focused_output, 'fake-2', 'focus on fake-2'); | |
165 | 158 | |
166 | 159 | cmd 'focus output left'; |
167 | is(get_focused_output(), 'fake-1', 'focus on fake-1'); | |
160 | is(focused_output, 'fake-1', 'focus on fake-1'); | |
168 | 161 | |
169 | 162 | cmd 'focus output left'; |
170 | is(get_focused_output(), 'fake-0', 'focus on fake-0'); | |
163 | is(focused_output, 'fake-0', 'focus on fake-0'); | |
171 | 164 | |
172 | 165 | cmd 'focus output left'; |
173 | is(get_focused_output(), 'fake-2', 'focus on fake-2 (wrapping)'); | |
166 | is(focused_output, 'fake-2', 'focus on fake-2 (wrapping)'); | |
174 | 167 | |
175 | 168 | cmd 'focus output right'; |
176 | is(get_focused_output(), 'fake-0', 'focus on fake-0 (wrapping)'); | |
169 | is(focused_output, 'fake-0', 'focus on fake-0 (wrapping)'); | |
177 | 170 | |
178 | 171 | exit_gracefully($pid); |
179 | 172 |
54 | 54 | |
55 | 55 | # Check that the windows are on the correct output |
56 | 56 | is_deeply(scalar $win_on_first_output->rect, $orig_rect1, "first window spans the first output"); |
57 | is_deeply(scalar $win_on_second_output->rect, $orig_rect2, "second window spans the sencond output"); | |
57 | is_deeply(scalar $win_on_second_output->rect, $orig_rect2, "second window spans the second output"); | |
58 | 58 | |
59 | 59 | # Check that both windows remained fullscreen |
60 | 60 | my $tree = i3(get_socket_path())->get_tree->recv; |
13 | 13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf |
14 | 14 | # (unless you are already familiar with Perl) |
15 | 15 | # |
16 | # Test using multiple workspaces for 'move workspace to output …' | |
16 | # Test using multiple outputs for 'move workspace to output …' | |
17 | 17 | # Ticket: #4337 |
18 | 18 | use i3test i3_config => <<EOT; |
19 | 19 | # i3 config file (v4) |
39 | 39 | } |
40 | 40 | |
41 | 41 | ############################################################################### |
42 | # Test moving workspace to same output | |
43 | # See issue #4691 | |
44 | ############################################################################### | |
45 | is_ws(1, 0, 'sanity check'); | |
46 | ||
47 | my $reply = cmd '[con_mark=aa] move workspace to output fake-0'; | |
48 | is_ws(1, 0, 'workspace did not move'); | |
49 | ok($reply->[0]->{success}, 'reply success'); | |
50 | ||
51 | ############################################################################### | |
42 | 52 | # Test using "next" special keyword |
43 | 53 | ############################################################################### |
44 | 54 | |
55 | 65 | } |
56 | 66 | |
57 | 67 | ############################################################################### |
58 | # Same as above but explicitely type all the outputs | |
68 | # Same as above but explicitly type all the outputs | |
59 | 69 | ############################################################################### |
60 | 70 | |
61 | 71 | is_ws(1, 0, 'sanity check'); |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test using multiple output for 'focus output …' | |
17 | # Ticket: #4619 | |
18 | use i3test i3_config => <<EOT; | |
19 | # i3 config file (v4) | |
20 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 | |
21 | ||
22 | fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+0+768,1024x768+1024+768 | |
23 | EOT | |
24 | ||
25 | ############################################################################### | |
26 | # Test using "next" special keyword | |
27 | ############################################################################### | |
28 | ||
29 | is(focused_output, "fake-0", 'sanity check'); | |
30 | ||
31 | for (my $i = 1; $i < 9; $i++) { | |
32 | cmd 'focus output next'; | |
33 | ||
34 | my $out = $i % 4; | |
35 | is(focused_output, "fake-$out", 'focus output next cycle'); | |
36 | } | |
37 | ||
38 | ############################################################################### | |
39 | # Same as above but explicitly type all the outputs | |
40 | ############################################################################### | |
41 | ||
42 | is(focused_output, "fake-0", 'sanity check'); | |
43 | ||
44 | for (my $i = 1; $i < 10; $i++) { | |
45 | cmd 'focus output fake-0 fake-1 fake-2 fake-3'; | |
46 | ||
47 | my $out = $i % 4; | |
48 | is(focused_output, "fake-$out", 'focus output next cycle'); | |
49 | } | |
50 | ||
51 | ############################################################################### | |
52 | # Use a subset of the outputs plus some non-existing outputs | |
53 | ############################################################################### | |
54 | ||
55 | cmd 'focus output fake-1'; | |
56 | is(focused_output, "fake-1", 'start from fake-1 which is not included in output list'); | |
57 | ||
58 | my @order = (0, 3, 2); | |
59 | for (my $i = 0; $i < 10; $i++) { | |
60 | cmd 'focus output doesnotexist fake-0 alsodoesnotexist fake-3 fake-2'; | |
61 | ||
62 | my $out = $order[$i % 3]; | |
63 | is(focused_output, "fake-$out", 'focus output next cycle'); | |
64 | } | |
65 | ||
66 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Tests whether our WM registration is done with the correct WM_S0 selection. | |
4 | ||
5 | use i3test; | |
6 | ||
7 | my $x = X11::XCB::Connection->new; | |
8 | my $reply = $x->get_selection_owner($x->atom(name => 'WM_S0')->id); | |
9 | ok($reply, "registration successful"); | |
10 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test that i3 doesn't crash if the binding command is empty. | |
17 | # Ticket: #5000 | |
18 | ||
19 | use i3test i3_autostart => 0; | |
20 | ||
21 | my $config = <<EOT; | |
22 | # i3 config file (v4) | |
23 | bindsym X | |
24 | EOT | |
25 | ||
26 | my $pid = launch_with_config($config); | |
27 | does_i3_live; | |
28 | ||
29 | exit_gracefully($pid); | |
30 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test that explicitly defined default mode doesn't cause segfault. | |
17 | # Ticket: #5006 | |
18 | # Bug still in: 4.20-96-gce2665ca | |
19 | ||
20 | use i3test i3_autostart => 0; | |
21 | ||
22 | my $config = <<EOT; | |
23 | # i3 config file (v4) | |
24 | mode "default" { | |
25 | bindsym X resize | |
26 | } | |
27 | EOT | |
28 | ||
29 | my $pid = launch_with_config($config); | |
30 | does_i3_live; | |
31 | ||
32 | exit_gracefully($pid); | |
33 | done_testing; |
0 | #!perl | |
1 | # vim:ts=4:sw=4:expandtab | |
2 | # | |
3 | # Please read the following documents before working on tests: | |
4 | # • https://build.i3wm.org/docs/testsuite.html | |
5 | # (or docs/testsuite) | |
6 | # | |
7 | # • https://build.i3wm.org/docs/lib-i3test.html | |
8 | # (alternatively: perldoc ./testcases/lib/i3test.pm) | |
9 | # | |
10 | # • https://build.i3wm.org/docs/ipc.html | |
11 | # (or docs/ipc) | |
12 | # | |
13 | # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf | |
14 | # (unless you are already familiar with Perl) | |
15 | # | |
16 | # Test that i3 doesn't crash if the config contains nested variables. | |
17 | # Ticket: #5002 | |
18 | ||
19 | use i3test i3_autostart => 0; | |
20 | ||
21 | ####################################################################### | |
22 | # Test calloc crash | |
23 | ####################################################################### | |
24 | ||
25 | my $config = <<'EOT'; | |
26 | # i3 config file (v4) | |
27 | set $long_variable_name_with_short_value 1 | |
28 | set $$long_variable_name_with_short_value 2 | |
29 | set $$$long_variable_name_with_short_value 3 | |
30 | EOT | |
31 | ||
32 | my $pid = launch_with_config($config); | |
33 | ||
34 | # ==2108678==ERROR: AddressSanitizer: requested allocation size 0xffffffffffffffe1 (0x7e8 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0) | |
35 | # #0 0x7feaa9cbf411 in __interceptor_calloc /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:77 | |
36 | # #1 0x560867c0c13d in scalloc ../libi3/safewrappers.c:29 | |
37 | # #2 0x560867b7bd63 in parse_file ../src/config_parser.c:1030 | |
38 | # #3 0x560867b6b1b8 in load_configuration ../src/config.c:261 | |
39 | # #4 0x560867baf9cf in main ../src/main.c:677 | |
40 | # #5 0x7feaa95b52cf (/usr/lib/libc.so.6+0x232cf) | |
41 | ||
42 | does_i3_live; | |
43 | ||
44 | exit_gracefully($pid); | |
45 | ||
46 | ||
47 | ####################################################################### | |
48 | # Test buffer overflow | |
49 | ####################################################################### | |
50 | ||
51 | $config = <<'EOT'; | |
52 | # i3 config file (v4) | |
53 | set $x 1 | |
54 | set $$x 2 | |
55 | EOT | |
56 | ||
57 | $pid = launch_with_config($config); | |
58 | ||
59 | # ==2110007==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6070000014f2 at pc 0x558590f680ba bp 0x7ffced72b760 sp 0x7ffced72b750 | |
60 | # WRITE of size 1 at 0x6070000014f2 thread T0 | |
61 | # #0 0x558590f680b9 in parse_file ../src/config_parser.c:1051 | |
62 | # #1 0x558590f571b8 in load_configuration ../src/config.c:261 | |
63 | # #2 0x558590f9b9cf in main ../src/main.c:677 | |
64 | # #3 0x7f81c61c82cf (/usr/lib/libc.so.6+0x232cf) | |
65 | # #4 0x7f81c61c8389 in __libc_start_main (/usr/lib/libc.so.6+0x23389) | |
66 | # #5 0x558590f0bd54 in _start ../sysdeps/x86_64/start.S:115 | |
67 | ||
68 | # 0x6070000014f2 is located 0 bytes to the right of 66-byte region [0x6070000014b0,0x6070000014f2) | |
69 | # allocated by thread T0 here: | |
70 | # #0 0x7f81c68bf411 in __interceptor_calloc /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:77 | |
71 | # #1 0x558590ff813d in scalloc ../libi3/safewrappers.c:29 | |
72 | # #2 0x558590f67d63 in parse_file ../src/config_parser.c:1030 | |
73 | # #3 0x558590f571b8 in load_configuration ../src/config.c:261 | |
74 | # #4 0x558590f9b9cf in main ../src/main.c:677 | |
75 | # #5 0x7f81c61c82cf (/usr/lib/libc.so.6+0x232cf) | |
76 | ||
77 | does_i3_live; | |
78 | ||
79 | exit_gracefully($pid); | |
80 | done_testing; |