From d29d908003b0acb0fa9fa0e9618186931423a20e Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Wed, 23 Aug 2017 15:48:58 +0200 Subject: [PATCH 001/187] Avoid use of uninitialized in init_dpi_end If conn == NULL or display == NULL, init_dpi() jumps to init_dpi_end before (declaring and) initializing resource. In init_dpi_end, there is a free(resource) call conditionally on resource != NULL, so this may lead to a bogus free. Found by clang -Wsometimes-uninitialized. --- libi3/dpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi3/dpi.c b/libi3/dpi.c index ce85cacc..93a3c6f6 100644 --- a/libi3/dpi.c +++ b/libi3/dpi.c @@ -24,6 +24,7 @@ static long init_dpi_fallback(void) { */ void init_dpi(void) { xcb_xrm_database_t *database = NULL; + char *resource = NULL; if (conn == NULL) { goto init_dpi_end; @@ -35,7 +36,6 @@ void init_dpi(void) { goto init_dpi_end; } - char *resource; xcb_xrm_resource_get_string(database, "Xft.dpi", NULL, &resource); if (resource == NULL) { DLOG("Resource Xft.dpi not specified, skipping.\n"); From 92b8196192a77bd39a76c966480047a76cf2b03f Mon Sep 17 00:00:00 2001 From: hwangcc23 Date: Thu, 31 Aug 2017 22:48:33 +0800 Subject: [PATCH 002/187] Properly initialize sigaction struct The code in handle_signal() wasn't clearing the struct sigaction before passing it to sigaction(). This meant that we would block a random set of signals while executing the default handler, or jump to the uninitialized __sa_sigaction__ (instead of sa_handler). Initialize properly as we do in setup_signal_handler(). --- src/sighandler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sighandler.c b/src/sighandler.c index b1e7d166..1a161d2a 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -305,6 +305,8 @@ void handle_signal(int sig, siginfo_t *info, void *data) { struct sigaction action; action.sa_handler = SIG_DFL; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); sigaction(sig, &action, NULL); raised_signal = sig; From 5df53f57c4bf594bbba59a4e906379559c5aebb1 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 4 Sep 2017 08:21:06 +0200 Subject: [PATCH 003/187] debian: update changelog --- debian/changelog | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 2efd65e8..bed38f8e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,14 @@ -i3-wm (4.13.1-1) unstable; urgency=medium +i3-wm (4.14.1-1) unstable; urgency=medium * UNRELEASED - -- Michael Stapelberg Tue, 08 Nov 2016 21:31:13 +0100 + -- Michael Stapelberg Mon, 04 Sep 2017 08:17:05 +0200 + +i3-wm (4.14-1) unstable; urgency=medium + + * New upstream release. + + -- Michael Stapelberg Mon, 04 Sep 2017 07:53:16 +0200 i3-wm (4.13-1) unstable; urgency=medium From 85eb09767704479297a4de5aff9c40690a0d89a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Tue, 5 Sep 2017 09:01:53 +0200 Subject: [PATCH 004/187] Invert condition to log debug message in correct situation (#2896) --- src/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.c b/src/commands.c index 393d7018..bbe7d265 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1854,7 +1854,7 @@ void cmd_swap(I3_CMD, const char *mode, const char *arg) { return; } - if (match == TAILQ_LAST(&owindows, owindows_head)) { + if (match != TAILQ_LAST(&owindows, owindows_head)) { DLOG("More than one container matched the swap command, only using the first one."); } From 66f2148236a47cafa6d0218faa3e7ad5bc8d949a Mon Sep 17 00:00:00 2001 From: Orestis Date: Wed, 6 Sep 2017 08:34:14 +0300 Subject: [PATCH 005/187] Check if con_id exists in cmd_swap (#2898) Also adds some testcases for swap using con_id. Fixes #2895 --- include/con.h | 7 +++++++ src/commands.c | 2 +- src/con.c | 16 ++++++++++++++++ testcases/t/265-swap.t | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/include/con.h b/include/con.h index 0fa660a1..1c7bb932 100644 --- a/include/con.h +++ b/include/con.h @@ -152,6 +152,13 @@ bool con_has_parent(Con *con, Con *parent); */ Con *con_by_window_id(xcb_window_t window); +/** + * Returns the container with the given container ID or NULL if no such + * container exists. + * + */ +Con *con_by_con_id(long target); + /** * Returns the container with the given frame ID or NULL if no such container * exists. diff --git a/src/commands.c b/src/commands.c index bbe7d265..faee3916 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1841,7 +1841,7 @@ void cmd_swap(I3_CMD, const char *mode, const char *arg) { return; } - con = (Con *)target; + con = con_by_con_id(target); } else if (strcmp(mode, "mark") == 0) { con = con_by_mark(arg); } else { diff --git a/src/con.c b/src/con.c index cf923ec8..b520c110 100644 --- a/src/con.c +++ b/src/con.c @@ -557,6 +557,22 @@ Con *con_by_window_id(xcb_window_t window) { return NULL; } +/* + * Returns the container with the given container ID or NULL if no such + * container exists. + * + */ +Con *con_by_con_id(long target) { + Con *con; + TAILQ_FOREACH(con, &all_cons, all_cons) { + if (con == (Con *)target) { + return con; + } + } + + return NULL; +} + /* * Returns the container with the given frame ID or NULL if no such container * exists. diff --git a/testcases/t/265-swap.t b/testcases/t/265-swap.t index f86bba71..e3c8e97c 100644 --- a/testcases/t/265-swap.t +++ b/testcases/t/265-swap.t @@ -32,6 +32,38 @@ my ($nodes, $expected_focus, $A, $B, $F); my ($result); my @urgent; +############################################################################### +# Invalid con_id should not crash i3 +# See issue #2895. +############################################################################### + +$pid = launch_with_config($config); +$ws = fresh_workspace; + +open_window; +cmd "swap container with con_id 1"; + +does_i3_live; +exit_gracefully($pid); + +############################################################################### +# Swap 2 windows in different workspaces using con_id +############################################################################### + +$pid = launch_with_config($config); + +$ws = fresh_workspace; +open_window; +$A = get_focused($ws); + +$ws = fresh_workspace; +open_window; + +cmd "swap container with con_id $A"; +is(get_focused($ws), $A, 'A is now focused'); + +exit_gracefully($pid); + ############################################################################### # Swap two containers next to each other. # Focus should stay on B because both windows are on the focused workspace. From b12cea0ad22b0a8b58ed26cccc1f70c59080eb63 Mon Sep 17 00:00:00 2001 From: Orestis Date: Wed, 6 Sep 2017 18:02:52 +0300 Subject: [PATCH 006/187] Fix typo in 265-swap.t --- testcases/t/265-swap.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/t/265-swap.t b/testcases/t/265-swap.t index e3c8e97c..92988187 100644 --- a/testcases/t/265-swap.t +++ b/testcases/t/265-swap.t @@ -189,7 +189,7 @@ $nodes = get_ws_content($ws1); is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left'); $nodes = get_ws_content($ws2); -is($nodes->[1]->{window}, $A->{id}, 'A is on ws1:right'); +is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right'); is(get_focused($ws2), $expected_focus, 'A is focused'); exit_gracefully($pid); From e5008ca0dc912e69b037f32a38e5272bd9d19585 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Thu, 7 Sep 2017 04:19:57 +0300 Subject: [PATCH 007/187] Improve 267-regress-mark-restart.t Another window with a mark is needed for issue #2900. --- testcases/t/267-regress-mark-restart.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testcases/t/267-regress-mark-restart.t b/testcases/t/267-regress-mark-restart.t index 220d765b..302d23e5 100644 --- a/testcases/t/267-regress-mark-restart.t +++ b/testcases/t/267-regress-mark-restart.t @@ -20,6 +20,8 @@ use i3test; cmd 'open'; cmd 'mark foo'; +cmd 'open'; +cmd 'mark bar'; cmd 'restart'; From 554775f694d322024a90f15fc62fd243687fc45d Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Thu, 7 Sep 2017 03:53:28 +0300 Subject: [PATCH 008/187] Set marks to NULL after freeing realloc() was being called on an already freed pointer. Fixes #2900 --- src/load_layout.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/load_layout.c b/src/load_layout.c index 632c6ec7..7961e17f 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -157,6 +157,7 @@ static int json_end_map(void *ctx) { } free(marks); + marks = NULL; num_marks = 0; } From 55964fb3bed4e06fd627ea57ab520b66fe298bbb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 06:56:50 +0200 Subject: [PATCH 009/187] i3bar: ensure get_buffer does not leak memory This fixes an AddressSanitizer warning which recently popped up. related to #2907 --- i3bar/src/child.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 814f0411..fe989c44 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -333,10 +333,12 @@ static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) { break; } ELOG("read() failed!: %s\n", strerror(errno)); + FREE(buffer); exit(EXIT_FAILURE); } if (n == 0) { ELOG("stdin: received EOF\n"); + FREE(buffer); *ret_buffer_len = -1; return NULL; } From 3137064efc577e90b342319365b173276b13d236 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 08:15:03 +0200 Subject: [PATCH 010/187] travis: downgrade temporarily due to asan issue fixes #2912 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 63f69ac8..de9ff3fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ sudo: false dist: trusty +# TODO: remove “group” once trusty kernel is no longer affected by +# https://github.com/google/sanitizers/issues/837 +group: deprecated-2017Q3 services: - docker language: c From 0875b1903466f7e59a7aa0222e89eab667f10672 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 14:22:16 +0200 Subject: [PATCH 011/187] Include AnyEvent-I3 directory in dist tarballs (#2916) fixes #2905 --- Makefile.am | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile.am b/Makefile.am index 3ea300e5..f1ba3f63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,19 @@ TESTS = testcases/complete-run.pl EXTRA_DIST = \ $(dist_docs_toc_DATA:.html=) \ $(dist_docs_notoc_DATA:.html=) \ + AnyEvent-I3/Changes \ + AnyEvent-I3/MANIFEST \ + AnyEvent-I3/MANIFEST.SKIP \ + AnyEvent-I3/Makefile.PL \ + AnyEvent-I3/README \ + AnyEvent-I3/lib/AnyEvent/I3.pm \ + AnyEvent-I3/t/00-load.t \ + AnyEvent-I3/t/01-workspaces.t \ + AnyEvent-I3/t/02-sugar.t \ + AnyEvent-I3/t/boilerplate.t \ + AnyEvent-I3/t/manifest.t \ + AnyEvent-I3/t/pod-coverage.t \ + AnyEvent-I3/t/pod.t \ docs/asciidoc-git.conf \ docs/bigpicture.png \ docs/i3-pod2html \ From 48fd6416c667bc7f5e7e48271d7fb3e24f8b6a7f Mon Sep 17 00:00:00 2001 From: Orestis Date: Sat, 9 Sep 2017 15:47:32 +0300 Subject: [PATCH 012/187] Add files generated by make check in AnyEvent-I3/ to .gitignore (#2915) --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index c6fd81c8..b2ebd4aa 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,13 @@ testcases/MYMETA.json testcases/MYMETA.yml testcases/blib/ testcases/pm_to_blib +AnyEvent-I3/Makefile +AnyEvent-I3/META.yml +AnyEvent-I3/MYMETA.json +AnyEvent-I3/MYMETA.yml +AnyEvent-I3/blib/ +AnyEvent-I3/inc/ +AnyEvent-I3/pm_to_blib *.output *.tab.* *.yy.c From 16160462a30f186f5b72bb551ba2188670d4e45c Mon Sep 17 00:00:00 2001 From: Jan Alexander Steffens Date: Sat, 9 Sep 2017 17:32:34 +0200 Subject: [PATCH 013/187] Use OVER operator for drawing text (#2908) For opaque text, SOURCE is not any different from OVER. However, when drawing color glyphs (which consist of RGBA pixels instead of strokes) SOURCE's handling of alpha is not what we want. I stumbled across this because cairo 1.15.8 seems to clear the surface before drawing color emoji if the operator is SOURCE, deleting every- thing drawn before. Arguably, the area outside the glyph bounds should not be touched, but even if this is a cairo bug the problem of alpha within the glyph remains. --- libi3/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi3/font.c b/libi3/font.c index 81091ea7..aef8427b 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -105,7 +105,7 @@ static void draw_text_pango(const char *text, size_t text_len, pango_layout_set_text(layout, text, text_len); /* Do the drawing */ - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); pango_cairo_update_layout(cr, layout); pango_layout_get_pixel_size(layout, NULL, &height); From c86307864f40da4a4ca69e9678695e02015c17b6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 17:50:23 +0200 Subject: [PATCH 014/187] =?UTF-8?q?t/265-swap:=20don=E2=80=99t=20start=20n?= =?UTF-8?q?ew=20i3=20instances=20with=20the=20same=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $config is never touched after being initially set up. Not restarting i3 between each test case reduces the runtime of this test by an order of magnitude. --- testcases/t/265-swap.t | 47 ++++-------------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/testcases/t/265-swap.t b/testcases/t/265-swap.t index 92988187..2927a622 100644 --- a/testcases/t/265-swap.t +++ b/testcases/t/265-swap.t @@ -32,26 +32,24 @@ my ($nodes, $expected_focus, $A, $B, $F); my ($result); my @urgent; +$pid = launch_with_config($config); + ############################################################################### # Invalid con_id should not crash i3 # See issue #2895. ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; open_window; cmd "swap container with con_id 1"; does_i3_live; -exit_gracefully($pid); ############################################################################### # Swap 2 windows in different workspaces using con_id ############################################################################### -$pid = launch_with_config($config); - $ws = fresh_workspace; open_window; $A = get_focused($ws); @@ -62,8 +60,6 @@ open_window; cmd "swap container with con_id $A"; is(get_focused($ws), $A, 'A is now focused'); -exit_gracefully($pid); - ############################################################################### # Swap two containers next to each other. # Focus should stay on B because both windows are on the focused workspace. @@ -73,7 +69,6 @@ exit_gracefully($pid); # | A | B | Focus Stacks: # +---+---+ H1: B, A ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -87,8 +82,6 @@ is($nodes->[0]->{window}, $B->{id}, 'B is on the left'); is($nodes->[1]->{window}, $A->{id}, 'A is on the right'); is(get_focused($ws), $expected_focus, 'B is still focused'); -exit_gracefully($pid); - ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is A. @@ -100,7 +93,6 @@ exit_gracefully($pid); # | Y | B | V1: A, Y # +---+---+ V2: B, X ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -120,8 +112,6 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); -exit_gracefully($pid); - ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is _not_ A. @@ -133,7 +123,6 @@ exit_gracefully($pid); # | Y | B | V1: Y, A # +---+---+ V2: B, X ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -153,8 +142,6 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); -exit_gracefully($pid); - ############################################################################### # Swap two containers with one being on a different workspace. # The focused container is B. @@ -171,8 +158,6 @@ exit_gracefully($pid); # | Y | B | Focus Stacks: # +---+---+ H2: B, Y ############################################################################### -$pid = launch_with_config($config); - $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $expected_focus = get_focused($ws1); @@ -192,8 +177,6 @@ $nodes = get_ws_content($ws2); is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right'); is(get_focused($ws2), $expected_focus, 'A is focused'); -exit_gracefully($pid); - ############################################################################### # Swap two non-focused containers within the same workspace. # @@ -203,7 +186,6 @@ exit_gracefully($pid); # | X | B | V1: A, X # +---+---+ V2: F, B ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -223,8 +205,6 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'F is still focused'); -exit_gracefully($pid); - ############################################################################### # Swap two non-focused containers which are both on different workspaces. # @@ -244,8 +224,6 @@ exit_gracefully($pid); # | F | # +---+ ############################################################################### -$pid = launch_with_config($config); - $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -266,8 +244,6 @@ is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace'); is(get_focused($ws3), $expected_focus, 'F is still focused'); -exit_gracefully($pid); - ############################################################################### # Swap two non-focused containers with one being on a different workspace. # @@ -283,7 +259,6 @@ exit_gracefully($pid); # | B | F | Focus Stacks: # +---+---+ H2: F, B ############################################################################### -$pid = launch_with_config($config); $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -302,8 +277,6 @@ $nodes = get_ws_content($ws2); is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace'); is(get_focused($ws2), $expected_focus, 'F is still focused'); -exit_gracefully($pid); - ############################################################################### # 1. A container cannot be swapped with its parent. # 2. A container cannot be swapped with one of its children. @@ -315,8 +288,6 @@ exit_gracefully($pid); # | | B | # +---+---+ ############################################################################### -$pid = launch_with_config($config); - $ws = fresh_workspace; open_window; open_window; @@ -330,8 +301,6 @@ is($result->[0]->{success}, 0, 'B cannot be swappd with its parent'); $result = cmd '[con_mark=A] swap container with mark B'; is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children'); -exit_gracefully($pid); - ############################################################################### # Swapping two containers preserves the geometry of the container they are # being swapped with. @@ -346,8 +315,6 @@ exit_gracefully($pid); # | B | A | # +---+-------+ ############################################################################### -$pid = launch_with_config($config); - $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); @@ -364,8 +331,6 @@ $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width'); cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width'); -exit_gracefully($pid); - ############################################################################### # Swapping containers not sharing the same parent preserves the geometry of # the container they are swapped with. @@ -388,7 +353,6 @@ exit_gracefully($pid); # | | X | # +---+-----+ ############################################################################### -$pid = launch_with_config($config); $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -411,12 +375,9 @@ $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height'); cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height'); -exit_gracefully($pid); - ############################################################################### # Swapping containers moves the urgency hint correctly. ############################################################################### -$pid = launch_with_config($config); $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); @@ -437,8 +398,8 @@ is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent'); is(@urgent, 0, 'A is not marked urgent'); is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent'); -exit_gracefully($pid); - ############################################################################### +exit_gracefully($pid); + done_testing; From ffd0ebd85c556a49309a92d4088725f85e26916d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 18:21:59 +0200 Subject: [PATCH 015/187] 165-for_window: merge config and re-use i3 instance, split remainder This reduces total test wall-clock time by 1.5s (from 7.5s down to 5.9s). --- testcases/t/165-for_window.t | 237 +++++--------------- testcases/t/165-for_window_tilingfloating.t | 49 ++++ 2 files changed, 109 insertions(+), 177 deletions(-) create mode 100644 testcases/t/165-for_window_tilingfloating.t diff --git a/testcases/t/165-for_window.t b/testcases/t/165-for_window.t index bc3df114..03eff9ae 100644 --- a/testcases/t/165-for_window.t +++ b/testcases/t/165-for_window.t @@ -19,6 +19,61 @@ use X11::XCB qw(PROP_MODE_REPLACE); my (@nodes); +my $config = <<'EOT'; +# i3 config file (v4) +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 + +# test 1, test 2 +for_window [class="borderless$"] border none +for_window [title="special borderless title"] border none + +# test 3 +for_window [class="borderless3$" title="usethis"] border none +for_window [class="borderless3$"] border none +for_window [title="special borderless title"] border none +for_window [title="special mark title"] border none, mark bleh + +# test 4 +for_window [class="borderless4$" title="usethis"] border none + +# test 5, test 6 +for_window [class="foo$"] border 1pixel + +# test 6 +for_window [instance="foo6"] border none + +# test 7 +for_window [id="asdf"] border none + +# test 8, test 9 +for_window [window_role="i3test"] border none + +# test 12 +for_window [workspace="trigger"] floating enable, mark triggered +EOT + +# test all window types +my %window_types = ( + 'normal' => '_NET_WM_WINDOW_TYPE_NORMAL', + 'dialog' => '_NET_WM_WINDOW_TYPE_DIALOG', + 'utility' => '_NET_WM_WINDOW_TYPE_UTILITY', + 'toolbar' => '_NET_WM_WINDOW_TYPE_TOOLBAR', + 'splash' => '_NET_WM_WINDOW_TYPE_SPLASH', + 'menu' => '_NET_WM_WINDOW_TYPE_MENU', + 'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU', + 'popup_menu' => '_NET_WM_WINDOW_TYPE_POPUP_MENU', + 'tooltip' => '_NET_WM_WINDOW_TYPE_TOOLTIP', + 'notification' => '_NET_WM_WINDOW_TYPE_NOTIFICATION' +); + +for my $window_type (keys %window_types) { + $config .= < 'Border window'); @@ -66,22 +112,11 @@ wait_for_unmap $window; @content = @{get_ws_content($tmp)}; cmp_ok(@content, '==', 0, 'no more nodes'); -exit_gracefully($pid); - ############################################################## # 2: match on the title, check if for_window is really executed # only once ############################################################## -$config = < 'special title'); @@ -116,23 +151,10 @@ wait_for_unmap $window; @content = @{get_ws_content($tmp)}; cmp_ok(@content, '==', 0, 'no more nodes'); -exit_gracefully($pid); - ############################################################## # 3: match on the title, set border style *and* a mark ############################################################## -$config = < 'special mark title'); @@ -154,25 +176,15 @@ cmd qq|[con_mark="bleh"] focus|; @content = @{get_ws_content($tmp)}; ok($content[0]->{focused}, 'first node focused'); -exit_gracefully($pid); - ############################################################## # 4: multiple criteria for the for_window command ############################################################## -$config = < 'usethis', - wm_class => 'borderless', + wm_class => 'borderless4', ); @content = @{get_ws_content($tmp)}; @@ -190,7 +202,7 @@ sync_with_i3; cmp_ok(@content, '==', 0, 'no nodes on this workspace now'); $window->_create; -$window->wm_class('borderless'); +$window->wm_class('borderless4'); $window->name('notthis'); $window->map; wait_for_map $window; @@ -199,24 +211,12 @@ wait_for_map $window; cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'normal', 'no border'); - -exit_gracefully($pid); - ############################################################## # 5: check that a class criterion does not match the instance ############################################################## -$config = < 'usethis', wm_class => 'bar', @@ -227,50 +227,26 @@ $window = open_window( cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'normal', 'normal border, not matched'); -exit_gracefully($pid); - ############################################################## # 6: check that the 'instance' criterion works ############################################################## -$config = < 'usethis', wm_class => 'bar', - instance => 'foo', + instance => 'foo6', ); @content = @{get_ws_content($tmp)}; cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'none', 'no border'); -exit_gracefully($pid); - ############################################################## # 7: check that invalid criteria don’t end up matching all windows ############################################################## -# this configuration is broken because "asdf" is not a valid integer -# the for_window should therefore recognize this error and don’t add the -# assignment -$config = <{border}, 'normal', 'normal border'); -exit_gracefully($pid); - ############################################################## # 8: check that the role criterion works properly ############################################################## -$config = <{border}, 'none', 'no border (window_role)'); -exit_gracefully($pid); - ############################################################## # 9: another test for the window_role, but this time it changes # *after* the window has been mapped ############################################################## -$config = < 'usethis'); @@ -364,45 +320,18 @@ sync_with_i3; cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'none', 'no border (window_role 2)'); -exit_gracefully($pid); - ############################################################## # 10: check that the criterion 'window_type' works ############################################################## -# test all window types -my %window_types = ( - 'normal' => '_NET_WM_WINDOW_TYPE_NORMAL', - 'dialog' => '_NET_WM_WINDOW_TYPE_DIALOG', - 'utility' => '_NET_WM_WINDOW_TYPE_UTILITY', - 'toolbar' => '_NET_WM_WINDOW_TYPE_TOOLBAR', - 'splash' => '_NET_WM_WINDOW_TYPE_SPLASH', - 'menu' => '_NET_WM_WINDOW_TYPE_MENU', - 'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU', - 'popup_menu' => '_NET_WM_WINDOW_TYPE_POPUP_MENU', - 'tooltip' => '_NET_WM_WINDOW_TYPE_TOOLTIP', - 'notification' => '_NET_WM_WINDOW_TYPE_NOTIFICATION' -); - while (my ($window_type, $atom) = each %window_types) { - - $config = <<"EOT"; -# i3 config file (v4) -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 -for_window [window_type="$window_type"] floating enable, mark branded -EOT - - $pid = launch_with_config($config); $tmp = fresh_workspace; $window = open_window(window_type => $x->atom(name => $atom)); my @nodes = @{get_ws($tmp)->{floating_nodes}}; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); - is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'branded' ], "mark set (window_type = $atom)"); - - exit_gracefully($pid); - + is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)"); } ############################################################## @@ -411,14 +340,6 @@ EOT ############################################################## while (my ($window_type, $atom) = each %window_types) { - - $config = <<"EOT"; -# i3 config file (v4) -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 -for_window [window_type="$window_type"] floating enable, mark branded -EOT - - $pid = launch_with_config($config); $tmp = fresh_workspace; $window = open_window(); @@ -432,24 +353,13 @@ EOT my @nodes = @{get_ws($tmp)->{floating_nodes}}; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); - is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'branded' ], "mark set (window_type = $atom)"); - - exit_gracefully($pid); - + is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)"); } ############################################################## # 12: check that the criterion 'workspace' works ############################################################## -$config = <<"EOT"; -# i3 config file (v4) -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 -for_window [workspace="trigger"] floating enable, mark triggered -EOT - -$pid = launch_with_config($config); - cmd 'workspace trigger'; $window = open_window; @@ -457,35 +367,8 @@ $window = open_window; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'triggered' ], "mark set for workspace criterion"); -exit_gracefully($pid); - ############################################################## -# 13: check that the tiling / floating criteria work. -############################################################## - -$config = <<"EOT"; -# i3 config file (v4) -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 -for_window [tiling] mark tiled -for_window [floating] mark floated -EOT - -$pid = launch_with_config($config); -$tmp = fresh_workspace; - -open_window; -open_floating_window; - -@nodes = @{get_ws($tmp)->{nodes}}; -cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); -is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion"); - -@nodes = @{get_ws($tmp)->{floating_nodes}}; -cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); -is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion"); exit_gracefully($pid); -############################################################## - done_testing; diff --git a/testcases/t/165-for_window_tilingfloating.t b/testcases/t/165-for_window_tilingfloating.t new file mode 100644 index 00000000..760ac53e --- /dev/null +++ b/testcases/t/165-for_window_tilingfloating.t @@ -0,0 +1,49 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +use i3test i3_autostart => 0; +use X11::XCB qw(PROP_MODE_REPLACE); + +############################################################## +# 13: check that the tiling / floating criteria work. +############################################################## + +my $config = <<"EOT"; +# i3 config file (v4) +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +for_window [tiling] mark tiled +for_window [floating] mark floated +EOT + +my $pid = launch_with_config($config); +my $tmp = fresh_workspace; + +open_window; +open_floating_window; + +my @nodes = @{get_ws($tmp)->{nodes}}; +cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); +is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion"); + +@nodes = @{get_ws($tmp)->{floating_nodes}}; +cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); +is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion"); + +exit_gracefully($pid); + +############################################################## + +done_testing; From 1a2ce3edaf4cb81acf1cda504151c8d96ff27a60 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 18:30:17 +0200 Subject: [PATCH 016/187] travis: remove clang-analyze (#2917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • The output currently contains a large number of false-positives and — AFAICT — no actual issues. • Upstream shows little interest in addressing the long-standing issues with the TAILQ macros, so the false-positive situation probably won’t change soon: https://bugs.llvm.org/show_bug.cgi?id=18222 Currently, we largely spend travis CPU cycles on this, for no additional value. --- .travis.yml | 1 - travis/clang-analyze.sh | 9 --------- travis/deploy-github-pages.sh | 1 - 3 files changed, 11 deletions(-) delete mode 100755 travis/clang-analyze.sh diff --git a/.travis.yml b/.travis.yml index de9ff3fc..87c996fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,6 @@ script: - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU} ./travis/debian-build.sh deb/ubuntu-amd64/DIST - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_386} linux32 ./travis/debian-build.sh deb/debian-i386/DIST - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU_386} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST - - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/clang-analyze.sh - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/docs.sh - ./travis/skip-pkg.sh || travis/prep-bintray.sh diff --git a/travis/clang-analyze.sh b/travis/clang-analyze.sh deleted file mode 100755 index 5ef390d3..00000000 --- a/travis/clang-analyze.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -set -e -set -x - -mkdir -p deb/DIST-clang/build -tar xf build/*.tar.bz2 -C deb/DIST-clang --strip-components=1 -(cd deb/DIST-clang/build && scan-build -o ../../CLANG ../configure && scan-build -o ../../CLANG --html-title="Analysis of i3 v$(git describe --tags)" make -j8) -mv deb/CLANG/*/* deb/CLANG diff --git a/travis/deploy-github-pages.sh b/travis/deploy-github-pages.sh index f86fbf22..2d454820 100755 --- a/travis/deploy-github-pages.sh +++ b/travis/deploy-github-pages.sh @@ -6,7 +6,6 @@ set -x GITVERSION=$(git describe --tags) mkdir build.i3wm.org -cp -r deb/CLANG build.i3wm.org/clang-analyze cp -r deb/COPY-DOCS build.i3wm.org/docs cd build.i3wm.org echo build.i3wm.org > CNAME From 899ffd872f3b23f30902aaa2e374734b57b61312 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 9 Sep 2017 18:39:56 +0200 Subject: [PATCH 017/187] 529-net-wm-desktop: avoid timeout, avoid restarts, split This shaves off almost half a second of the wall-clock time (from 5.9s to 5.6s). --- testcases/t/529-net-wm-desktop.t | 86 ++++------------------------- testcases/t/529-net-wm-desktop_mm.t | 75 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 74 deletions(-) create mode 100644 testcases/t/529-net-wm-desktop_mm.t diff --git a/testcases/t/529-net-wm-desktop.t b/testcases/t/529-net-wm-desktop.t index 8f2df735..42b488d9 100644 --- a/testcases/t/529-net-wm-desktop.t +++ b/testcases/t/529-net-wm-desktop.t @@ -66,8 +66,15 @@ sub open_window_with_net_wm_desktop { pack('L', $idx), ); }, + dont_map => 1, ); + # We don’t wait for MapNotify and instead sync with i3 so that we don’t need + # to encounter the full timeout of 4s when opening a window on a non-visible + # workspace. + $window->map; + sync_with_i3; + return $window; } @@ -80,9 +87,7 @@ sub kill_windows { ############################################################################### -my ($config, $config_mm, $pid, $con); - -$config = <{floating_nodes}}, 1, 'The window is floating'); ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when the window is moved to another workspace # on the same output. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; open_window; cmd 'workspace 1'; @@ -178,15 +160,12 @@ cmd 'move window to workspace 1'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when the floating window is moved to another # workspace on the same output. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; open_window; cmd 'workspace 1'; @@ -200,35 +179,11 @@ cmd 'move window to workspace 1'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window'); kill_windows; -exit_gracefully($pid); - -############################################################################### -# _NET_WM_DESKTOP is updated when the window is moved to another workspace -# on another output. -############################################################################### - -$pid = launch_with_config($config_mm); - -cmd 'workspace 0'; -open_window; -cmd 'workspace 10'; -open_window; -cmd 'workspace 0'; -$con = open_window; - -cmd 'move window to workspace 10'; - -is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window'); - -kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is removed when the window is withdrawn. ############################################################################### -$pid = launch_with_config($config); - $con = open_window; is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set (sanity check)'); @@ -238,15 +193,12 @@ wait_for_unmap($con); is(get_net_wm_desktop($con), undef, '_NET_WM_DESKTOP is removed'); kill_windows; -exit_gracefully($pid); ############################################################################### # A _NET_WM_DESKTOP client message sent to the root window moves a window # to the correct workspace. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; open_window; cmd 'workspace 1'; @@ -263,15 +215,12 @@ is_num_children('1', 2, 'The window is now on workspace 1'); is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated'); kill_windows; -exit_gracefully($pid); ############################################################################### # A _NET_WM_DESKTOP client message sent to the root window can make a window # sticky. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; $con = open_window; @@ -282,15 +231,12 @@ is(@{get_ws('0')->{floating_nodes}}, 1, 'The window is floating'); ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when a new workspace with a lower number is # opened and closed. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 1'; $con = open_window; is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); @@ -299,14 +245,11 @@ cmd 'workspace 0'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when a window is made sticky by command. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; $con = open_window; cmd 'floating enable'; @@ -316,14 +259,11 @@ cmd 'sticky enable'; is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when a window is made sticky by client message. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; $con = open_window; cmd 'floating enable'; @@ -343,14 +283,11 @@ sync_with_i3; is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); kill_windows; -exit_gracefully($pid); ############################################################################### # _NET_WM_DESKTOP is updated when a window is moved to the scratchpad. ############################################################################### -$pid = launch_with_config($config); - cmd 'workspace 0'; $con = open_window; cmd 'floating enable'; @@ -363,8 +300,9 @@ cmd 'scratchpad show'; is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); kill_windows; -exit_gracefully($pid); ############################################################################### +exit_gracefully($pid); + done_testing; diff --git a/testcases/t/529-net-wm-desktop_mm.t b/testcases/t/529-net-wm-desktop_mm.t new file mode 100644 index 00000000..77238946 --- /dev/null +++ b/testcases/t/529-net-wm-desktop_mm.t @@ -0,0 +1,75 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests for _NET_WM_DESKTOP. +# Ticket: #2153 +use i3test i3_autostart => 0; +use X11::XCB qw(:all); + +sub get_net_wm_desktop { + sync_with_i3; + + my ($con) = @_; + my $cookie = $x->get_property( + 0, + $con->{id}, + $x->atom(name => '_NET_WM_DESKTOP')->id, + $x->atom(name => 'CARDINAL')->id, + 0, + 1 + ); + + my $reply = $x->get_property_reply($cookie->{sequence}); + return undef if $reply->{length} != 1; + + return unpack("L", $reply->{value}); +} + +my $config = < Date: Sun, 10 Sep 2017 05:43:53 +0000 Subject: [PATCH 018/187] Add README.md (#2910) --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..21b2e24b --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +![Logo](docs/logo-30.png) i3: A tiling window manager +===================================================== + +[![Build Status](https://travis-ci.org/i3/i3.svg?branch=next)](https://travis-ci.org/i3/i3) +[![Issue Stats](http://www.issuestats.com/github/i3/i3/badge/issue?style=flat)](http://www.issuestats.com/github/i3/i3) +[![Pull Request Stats](http://www.issuestats.com/github/i3/i3/badge/pr?style=flat)](http://www.issuestats.com/github/i3/i3) + +i3 is a tiling window manager for X11. + +For more information about i3, please see [the project's website](https://i3wm.org/) and [online documentation](https://i3wm.org/docs/). + +For information about contributing to i3, please see [CONTRIBUTING.md](.github/CONTRIBUTING.md). From 6caf4e1cb52b72745a7c497caf0dbd448f9e67f9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 11:25:43 +0200 Subject: [PATCH 019/187] testcases/Makefile.PL: tell MakeMaker this is a pure-Perl distribution (#2922) fixes #2914 --- testcases/Makefile.PL | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testcases/Makefile.PL b/testcases/Makefile.PL index 0b1f3055..bf4e5dd7 100755 --- a/testcases/Makefile.PL +++ b/testcases/Makefile.PL @@ -18,7 +18,11 @@ WriteMakefile( PM => {}, # do not install any files from this directory clean => { FILES => 'testsuite-* latest i3-cfg-for-*', - } + }, + # This is a pure-Perl distribution: + linkext => { + LINKTYPE => '', + }, ); package MY; From 94c76d9e3090e3abb8d66811b082d4290f1a3152 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 11:30:56 +0200 Subject: [PATCH 020/187] i3test: add kill_all_windows convenience function --- testcases/lib/i3test.pm.in | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index 18bebb52..4046e620 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -46,6 +46,7 @@ our @EXPORT = qw( wait_for_map wait_for_unmap $x + kill_all_windows ); =head1 NAME @@ -899,6 +900,17 @@ sub get_i3_log { return slurp($logfile); } +=head2 kill_all_windows + +Kills all windows to clean up between tests. + +=cut +sub kill_all_windows { + # Sync in case not all windows are managed by i3 just yet. + sync_with_i3; + cmd '[title=".*"] kill'; +} + =head1 AUTHOR Michael Stapelberg From f7565b5f3238f1229608131e6358350538e262fa Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 11:31:10 +0200 Subject: [PATCH 021/187] Kill windows between tests --- testcases/t/165-for_window.t | 24 ++++++++++++++++++++++++ testcases/t/265-swap.t | 25 +++++++++++++++++++++++++ testcases/t/529-net-wm-desktop.t | 31 ++++++++++++------------------- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/testcases/t/165-for_window.t b/testcases/t/165-for_window.t index 03eff9ae..984cd6da 100644 --- a/testcases/t/165-for_window.t +++ b/testcases/t/165-for_window.t @@ -112,6 +112,8 @@ wait_for_unmap $window; @content = @{get_ws_content($tmp)}; cmp_ok(@content, '==', 0, 'no more nodes'); +kill_all_windows; + ############################################################## # 2: match on the title, check if for_window is really executed # only once @@ -151,6 +153,8 @@ wait_for_unmap $window; @content = @{get_ws_content($tmp)}; cmp_ok(@content, '==', 0, 'no more nodes'); +kill_all_windows; + ############################################################## # 3: match on the title, set border style *and* a mark ############################################################## @@ -176,6 +180,8 @@ cmd qq|[con_mark="bleh"] focus|; @content = @{get_ws_content($tmp)}; ok($content[0]->{focused}, 'first node focused'); +kill_all_windows; + ############################################################## # 4: multiple criteria for the for_window command ############################################################## @@ -211,6 +217,8 @@ wait_for_map $window; cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'normal', 'no border'); +kill_all_windows; + ############################################################## # 5: check that a class criterion does not match the instance ############################################################## @@ -227,6 +235,8 @@ $window = open_window( cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'normal', 'normal border, not matched'); +kill_all_windows; + ############################################################## # 6: check that the 'instance' criterion works ############################################################## @@ -243,6 +253,8 @@ $window = open_window( cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'none', 'no border'); +kill_all_windows; + ############################################################## # 7: check that invalid criteria don’t end up matching all windows ############################################################## @@ -259,6 +271,8 @@ $window = open_window( cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'normal', 'normal border'); +kill_all_windows; + ############################################################## # 8: check that the role criterion works properly ############################################################## @@ -287,6 +301,8 @@ $window = open_window( cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'none', 'no border (window_role)'); +kill_all_windows; + ############################################################## # 9: another test for the window_role, but this time it changes # *after* the window has been mapped @@ -320,6 +336,8 @@ sync_with_i3; cmp_ok(@content, '==', 1, 'one node on this workspace now'); is($content[0]->{border}, 'none', 'no border (window_role 2)'); +kill_all_windows; + ############################################################## # 10: check that the criterion 'window_type' works ############################################################## @@ -332,6 +350,8 @@ while (my ($window_type, $atom) = each %window_types) { my @nodes = @{get_ws($tmp)->{floating_nodes}}; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)"); + + kill_all_windows; } ############################################################## @@ -354,6 +374,8 @@ while (my ($window_type, $atom) = each %window_types) { my @nodes = @{get_ws($tmp)->{floating_nodes}}; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)"); + + kill_all_windows; } ############################################################## @@ -367,6 +389,8 @@ $window = open_window; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'triggered' ], "mark set for workspace criterion"); +kill_all_windows; + ############################################################## exit_gracefully($pid); diff --git a/testcases/t/265-swap.t b/testcases/t/265-swap.t index 2927a622..810c39af 100644 --- a/testcases/t/265-swap.t +++ b/testcases/t/265-swap.t @@ -45,6 +45,7 @@ open_window; cmd "swap container with con_id 1"; does_i3_live; +kill_all_windows; ############################################################################### # Swap 2 windows in different workspaces using con_id @@ -60,6 +61,8 @@ open_window; cmd "swap container with con_id $A"; is(get_focused($ws), $A, 'A is now focused'); +kill_all_windows; + ############################################################################### # Swap two containers next to each other. # Focus should stay on B because both windows are on the focused workspace. @@ -82,6 +85,8 @@ is($nodes->[0]->{window}, $B->{id}, 'B is on the left'); is($nodes->[1]->{window}, $A->{id}, 'A is on the right'); is(get_focused($ws), $expected_focus, 'B is still focused'); +kill_all_windows; + ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is A. @@ -112,6 +117,8 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); +kill_all_windows; + ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is _not_ A. @@ -142,6 +149,8 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); +kill_all_windows; + ############################################################################### # Swap two containers with one being on a different workspace. # The focused container is B. @@ -177,6 +186,8 @@ $nodes = get_ws_content($ws2); is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right'); is(get_focused($ws2), $expected_focus, 'A is focused'); +kill_all_windows; + ############################################################################### # Swap two non-focused containers within the same workspace. # @@ -205,6 +216,8 @@ is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'F is still focused'); +kill_all_windows; + ############################################################################### # Swap two non-focused containers which are both on different workspaces. # @@ -244,6 +257,8 @@ is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace'); is(get_focused($ws3), $expected_focus, 'F is still focused'); +kill_all_windows; + ############################################################################### # Swap two non-focused containers with one being on a different workspace. # @@ -277,6 +292,8 @@ $nodes = get_ws_content($ws2); is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace'); is(get_focused($ws2), $expected_focus, 'F is still focused'); +kill_all_windows; + ############################################################################### # 1. A container cannot be swapped with its parent. # 2. A container cannot be swapped with one of its children. @@ -301,6 +318,8 @@ is($result->[0]->{success}, 0, 'B cannot be swappd with its parent'); $result = cmd '[con_mark=A] swap container with mark B'; is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children'); +kill_all_windows; + ############################################################################### # Swapping two containers preserves the geometry of the container they are # being swapped with. @@ -331,6 +350,8 @@ $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width'); cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width'); +kill_all_windows; + ############################################################################### # Swapping containers not sharing the same parent preserves the geometry of # the container they are swapped with. @@ -375,6 +396,8 @@ $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height'); cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height'); +kill_all_windows; + ############################################################################### # Swapping containers moves the urgency hint correctly. ############################################################################### @@ -398,6 +421,8 @@ is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent'); is(@urgent, 0, 'A is not marked urgent'); is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent'); +kill_all_windows; + ############################################################################### exit_gracefully($pid); diff --git a/testcases/t/529-net-wm-desktop.t b/testcases/t/529-net-wm-desktop.t index 42b488d9..5f858051 100644 --- a/testcases/t/529-net-wm-desktop.t +++ b/testcases/t/529-net-wm-desktop.t @@ -78,13 +78,6 @@ sub open_window_with_net_wm_desktop { return $window; } -# We need to kill all windows in between tests since they survive the i3 restart -# and will interfere with the following tests. -sub kill_windows { - sync_with_i3; - cmd '[title="Window.*"] kill'; -} - ############################################################################### my $config = <{floating_nodes}}, 1, 'The window is floating'); ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when the window is moved to another workspace @@ -159,7 +152,7 @@ cmd 'move window to workspace 1'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when the floating window is moved to another @@ -178,7 +171,7 @@ cmd 'move window to workspace 1'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is removed when the window is withdrawn. @@ -192,7 +185,7 @@ wait_for_unmap($con); is(get_net_wm_desktop($con), undef, '_NET_WM_DESKTOP is removed'); -kill_windows; +kill_all_windows; ############################################################################### # A _NET_WM_DESKTOP client message sent to the root window moves a window @@ -214,7 +207,7 @@ is_num_children('0', 1, 'The window is no longer on workspace 0'); is_num_children('1', 2, 'The window is now on workspace 1'); is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated'); -kill_windows; +kill_all_windows; ############################################################################### # A _NET_WM_DESKTOP client message sent to the root window can make a window @@ -230,7 +223,7 @@ is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); is(@{get_ws('0')->{floating_nodes}}, 1, 'The window is floating'); ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when a new workspace with a lower number is @@ -244,7 +237,7 @@ is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); cmd 'workspace 0'; is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when a window is made sticky by command. @@ -258,7 +251,7 @@ is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); cmd 'sticky enable'; is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when a window is made sticky by client message. @@ -282,7 +275,7 @@ sync_with_i3; is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); -kill_windows; +kill_all_windows; ############################################################################### # _NET_WM_DESKTOP is updated when a window is moved to the scratchpad. @@ -299,7 +292,7 @@ is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); cmd 'scratchpad show'; is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); -kill_windows; +kill_all_windows; ############################################################################### From 828c759489798ee88b7231462d0bdbbed320b8a2 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 10 Sep 2017 11:02:10 +0000 Subject: [PATCH 022/187] docs/testsuite: Correct Xephyr package name on Arch Linux (#2913) The package is called `xorg-server-xephyr`, not `xorg-xserver-xephyr`. --- docs/testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/testsuite b/docs/testsuite index 795be042..4fa3e9e1 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -75,7 +75,7 @@ used to install the testsuite. Many users prefer to use the more modern +cpanminus+ instead, though (because it asks no questions and just works): The tests additionally require +Xephyr(1)+ to run a nested X server. Install -+xserver-xephyr+ on Debian or +xorg-xserver-xephyr+ on Arch Linux. ++xserver-xephyr+ on Debian or +xorg-server-xephyr+ on Arch Linux. .Installing testsuite dependencies using cpanminus (preferred) -------------------------------------------------------------------------------- From eba177342f00ee6efdc3eff05665f317e1a837ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Sun, 10 Sep 2017 17:03:09 +0200 Subject: [PATCH 023/187] Revert "Use OVER operator for drawing text (#2908)" (#2925) This reverts commit 16160462a30f186f5b72bb551ba2188670d4e45c. --- libi3/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi3/font.c b/libi3/font.c index aef8427b..81091ea7 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -105,7 +105,7 @@ static void draw_text_pango(const char *text, size_t text_len, pango_layout_set_text(layout, text, text_len); /* Do the drawing */ - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); pango_cairo_update_layout(cr, layout); pango_layout_get_pixel_size(layout, NULL, &height); From bed5c9e03a2c3e36e8d7e51ae24293d132a4f2c1 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 19:41:49 +0200 Subject: [PATCH 024/187] tests: re-seed random number generator in workers --- testcases/lib/TestWorker.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testcases/lib/TestWorker.pm b/testcases/lib/TestWorker.pm index aee994f7..c56767c4 100644 --- a/testcases/lib/TestWorker.pm +++ b/testcases/lib/TestWorker.pm @@ -99,6 +99,11 @@ sub worker_wait { $0 = $file; + # Re-seed rand() so that File::Temp’s tempnam produces different + # results, making a TOCTOU between e.g. t/175-startup-notification.t + # and t/180-fd-leaks.t less likely. + srand(time ^ $$); + POSIX::dup2($ipc_fd, 0); POSIX::dup2($ipc_fd, 1); POSIX::dup2(1, 2); From 06e8f75d529e821690a816053268f76dd05b30a8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 20:06:49 +0200 Subject: [PATCH 025/187] tests: unflake t/263-edge-borders.t --- testcases/t/263-edge-borders.t | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testcases/t/263-edge-borders.t b/testcases/t/263-edge-borders.t index 0d14c65c..6da1dacd 100644 --- a/testcases/t/263-edge-borders.t +++ b/testcases/t/263-edge-borders.t @@ -147,6 +147,10 @@ ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers'); $wscontent = get_ws($tmp); +# Ensure i3’s X11 requests are processed before our inquiry via +# $tilewindow->rect: +sync_with_i3; + @tiled = @{$wscontent->{nodes}}; ok(@tiled == 2, 'two tiled container opened in another container'); is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1'); From 1c94d189c3acb57b2e374375938cd813aba68fa6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Sep 2017 20:05:55 +0200 Subject: [PATCH 026/187] tests: run 533-randr15.t at the very end The test runs `xrandr setmonitor`, which will otherwise affect any test scheduled after 533-randr15.t, causing flakyness in t/217-NET_CURRENT_DESKTOP.t for example. --- testcases/complete-run.pl.in | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/testcases/complete-run.pl.in b/testcases/complete-run.pl.in index ba192469..ddd6ccad 100755 --- a/testcases/complete-run.pl.in +++ b/testcases/complete-run.pl.in @@ -177,11 +177,18 @@ my $timingsjson = slurp('.last_run_timings.json') if -e '.last_run_timings.json' map { [$_, $timings{$_} // 999] } @testfiles; # Run 000-load-deps.t first to bail out early when dependencies are missing. -my $loadtest = "t/000-load-deps.t"; -if ((scalar grep { $_ eq $loadtest } @testfiles) > 0) { +my ($loadtest) = grep { $_ =~ m,t/000-load-deps.t$, } @testfiles; +if (defined($loadtest)) { @testfiles = ($loadtest, grep { $_ ne $loadtest } @testfiles); } +# Run 533-randr15.t last because it destructively modifies the RandR +# configuration of the X session, interfering with any test started afterwards. +my ($randrtest) = grep { $_ =~ m,t/533-randr15.t$, } @testfiles; +if (defined($randrtest)) { + @testfiles = ((grep { $_ ne $randrtest } @testfiles), $randrtest); +} + printf("\nRough time estimate for this run: %.2f seconds\n\n", $timings{GLOBAL}) if exists($timings{GLOBAL}); From 5c693ec2ae124187cf5c0f05bcd1b55b1cdd5050 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 11 Sep 2017 13:04:58 +0000 Subject: [PATCH 027/187] docs/hacking-howto: Promote "Using git / sending patches" section Move the contents of the "Using git / sending patches" section to the top of the document. --- docs/hacking-howto | 160 ++++++++++++++++++++++----------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/docs/hacking-howto b/docs/hacking-howto index 52436da6..6842ce81 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -8,6 +8,86 @@ touching i3’s source code. It should contain all important information to help you understand why things are like they are. If it does not mention something you find necessary, please do not hesitate to contact me. +== Using git / sending patches + +=== Introduction + +For a short introduction into using git, see +http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy +or, for more documentation, see http://git-scm.com/documentation + +Please talk to us before working on new features to see whether they will be +accepted. A good way for this is to open an issue and asking for opinions on it. +Even for accepted features, this can be a good way to refine an idea upfront. However, +we don't want to see certain features in i3, e.g., switching window focus in an +Alt+Tab like way. + +When working on bugfixes, please make sure you mention that you are working on +it in the corresponding bug report at https://github.com/i3/i3/issues. In case +there is no bug report yet, please create one. + +After you are done, please submit your work for review as a pull request at +https://github.com/i3/i3. + +Do not send emails to the mailing list or any author directly, and don’t submit +them in the bugtracker, since all reviews should be done in public at +https://github.com/i3/i3. In order to make your review go as fast as possible, you +could have a look at previous reviews and see what the common mistakes are. + +=== Which branch to use? + +Work on i3 generally happens in two branches: “master” and “next” (the latter +being the default branch, the one that people get when they check out the git +repository). + +The contents of “master” are always stable. That is, it contains the source code +of the latest release, plus any bugfixes that were applied since that release. + +New features are only found in the “next” branch. Therefore, if you are working +on a new feature, use the “next” branch. If you are working on a bugfix, use the +“next” branch, too, but make sure your code also works on “master”. + +=== How to build? + +You can build i3 like you build any other software package which uses autotools. +Here’s a memory refresher: + + $ autoreconf -fi + $ mkdir -p build && cd build + $ ../configure + $ make -j8 + +(The autoreconf -fi step is unnecessary if you are building from a release tarball, + but shouldn’t hurt either.) + +==== Build system features + +* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate + directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building + in a separate directory is common practice anyway. In case this causes any + trouble when packaging i3 for your distribution, please open an issue. + +* “make check” runs the i3 testsuite. See docs/testsuite for details. + +* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker + feedback cycle than waiting for the travis build to catch the issue). + +* “make uninstall” (occasionally requested by users who compile from source) + +* “make” will build manpages/docs by default if the tools are installed. + Conversely, manpages/docs are not tried to be built for users who don’t want + to install all these dependencies to get started hacking on i3. + +* non-release builds will enable address sanitizer by default. Use the + --disable-sanitizers configure option to turn off all sanitizers, and see + --help for available sanitizers. + +* Support for pre-compiled headers (PCH) has been dropped for now in the + interest of simplicity. If you need support for PCH, please open an issue. + +* Coverage reports are now generated using “make check-code-coverage”, which + requires specifying --enable-code-coverage when calling configure. + == Window Managers A window manager is not necessarily needed to run X, but it is usually used in @@ -951,86 +1031,6 @@ Without much ado, here is the list of cases which need to be considered: not relative to workspace boundaries, so you must correct their coordinates or those containers will show up in the wrong workspace or not at all. -== Using git / sending patches - -=== Introduction - -For a short introduction into using git, see -http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy -or, for more documentation, see http://git-scm.com/documentation - -Please talk to us before working on new features to see whether they will be -accepted. A good way for this is to open an issue and asking for opinions on it. -Even for accepted features, this can be a good way to refine an idea upfront. However, -we don't want to see certain features in i3, e.g., switching window focus in an -Alt+Tab like way. - -When working on bugfixes, please make sure you mention that you are working on -it in the corresponding bug report at https://github.com/i3/i3/issues. In case -there is no bug report yet, please create one. - -After you are done, please submit your work for review as a pull request at -https://github.com/i3/i3. - -Do not send emails to the mailing list or any author directly, and don’t submit -them in the bugtracker, since all reviews should be done in public at -https://github.com/i3/i3. In order to make your review go as fast as possible, you -could have a look at previous reviews and see what the common mistakes are. - -=== Which branch to use? - -Work on i3 generally happens in two branches: “master” and “next” (the latter -being the default branch, the one that people get when they check out the git -repository). - -The contents of “master” are always stable. That is, it contains the source code -of the latest release, plus any bugfixes that were applied since that release. - -New features are only found in the “next” branch. Therefore, if you are working -on a new feature, use the “next” branch. If you are working on a bugfix, use the -“next” branch, too, but make sure your code also works on “master”. - -=== How to build? - -You can build i3 like you build any other software package which uses autotools. -Here’s a memory refresher: - - $ autoreconf -fi - $ mkdir -p build && cd build - $ ../configure - $ make -j8 - -(The autoreconf -fi step is unnecessary if you are building from a release tarball, - but shouldn’t hurt either.) - -==== Build system features - -* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate - directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building - in a separate directory is common practice anyway. In case this causes any - trouble when packaging i3 for your distribution, please open an issue. - -* “make check” runs the i3 testsuite. See docs/testsuite for details. - -* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker - feedback cycle than waiting for the travis build to catch the issue). - -* “make uninstall” (occasionally requested by users who compile from source) - -* “make” will build manpages/docs by default if the tools are installed. - Conversely, manpages/docs are not tried to be built for users who don’t want - to install all these dependencies to get started hacking on i3. - -* non-release builds will enable address sanitizer by default. Use the - --disable-sanitizers configure option to turn off all sanitizers, and see - --help for available sanitizers. - -* Support for pre-compiled headers (PCH) has been dropped for now in the - interest of simplicity. If you need support for PCH, please open an issue. - -* Coverage reports are now generated using “make check-code-coverage”, which - requires specifying --enable-code-coverage when calling configure. - == Thought experiments In this section, we collect thought experiments, so that we don’t forget our From e799bda2daa534646d53a80f795b881796d4bb3f Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 11 Sep 2017 13:06:40 +0000 Subject: [PATCH 028/187] docs/hacking-howto: Promote "How to build?" sub-section Move the "How to build?" sub-section to the top of its parent section. --- docs/hacking-howto | 74 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/hacking-howto b/docs/hacking-howto index 6842ce81..9c89947b 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -10,43 +10,6 @@ you find necessary, please do not hesitate to contact me. == Using git / sending patches -=== Introduction - -For a short introduction into using git, see -http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy -or, for more documentation, see http://git-scm.com/documentation - -Please talk to us before working on new features to see whether they will be -accepted. A good way for this is to open an issue and asking for opinions on it. -Even for accepted features, this can be a good way to refine an idea upfront. However, -we don't want to see certain features in i3, e.g., switching window focus in an -Alt+Tab like way. - -When working on bugfixes, please make sure you mention that you are working on -it in the corresponding bug report at https://github.com/i3/i3/issues. In case -there is no bug report yet, please create one. - -After you are done, please submit your work for review as a pull request at -https://github.com/i3/i3. - -Do not send emails to the mailing list or any author directly, and don’t submit -them in the bugtracker, since all reviews should be done in public at -https://github.com/i3/i3. In order to make your review go as fast as possible, you -could have a look at previous reviews and see what the common mistakes are. - -=== Which branch to use? - -Work on i3 generally happens in two branches: “master” and “next” (the latter -being the default branch, the one that people get when they check out the git -repository). - -The contents of “master” are always stable. That is, it contains the source code -of the latest release, plus any bugfixes that were applied since that release. - -New features are only found in the “next” branch. Therefore, if you are working -on a new feature, use the “next” branch. If you are working on a bugfix, use the -“next” branch, too, but make sure your code also works on “master”. - === How to build? You can build i3 like you build any other software package which uses autotools. @@ -88,6 +51,43 @@ Here’s a memory refresher: * Coverage reports are now generated using “make check-code-coverage”, which requires specifying --enable-code-coverage when calling configure. +=== Introduction + +For a short introduction into using git, see +http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy +or, for more documentation, see http://git-scm.com/documentation + +Please talk to us before working on new features to see whether they will be +accepted. A good way for this is to open an issue and asking for opinions on it. +Even for accepted features, this can be a good way to refine an idea upfront. However, +we don't want to see certain features in i3, e.g., switching window focus in an +Alt+Tab like way. + +When working on bugfixes, please make sure you mention that you are working on +it in the corresponding bug report at https://github.com/i3/i3/issues. In case +there is no bug report yet, please create one. + +After you are done, please submit your work for review as a pull request at +https://github.com/i3/i3. + +Do not send emails to the mailing list or any author directly, and don’t submit +them in the bugtracker, since all reviews should be done in public at +https://github.com/i3/i3. In order to make your review go as fast as possible, you +could have a look at previous reviews and see what the common mistakes are. + +=== Which branch to use? + +Work on i3 generally happens in two branches: “master” and “next” (the latter +being the default branch, the one that people get when they check out the git +repository). + +The contents of “master” are always stable. That is, it contains the source code +of the latest release, plus any bugfixes that were applied since that release. + +New features are only found in the “next” branch. Therefore, if you are working +on a new feature, use the “next” branch. If you are working on a bugfix, use the +“next” branch, too, but make sure your code also works on “master”. + == Window Managers A window manager is not necessarily needed to run X, but it is usually used in From 7116bbaa12678a889cb22b3841a275d6c9acc588 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 11 Sep 2017 13:09:25 +0000 Subject: [PATCH 029/187] docs/hacking-howto: Update section topology - Promote the "How to build?" sub-section to a top-level section ("Building i3") - Convert the "Introduction" sub-section as the intro to the remaining contents of the "Using git / sending patches" section - Keep "Which branch to use?" as a level-3 sub-section, thus making it a sub-section of what used to be the "Introduction" sub-section. --- docs/hacking-howto | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/hacking-howto b/docs/hacking-howto index 9c89947b..d585c2d7 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -8,9 +8,7 @@ touching i3’s source code. It should contain all important information to help you understand why things are like they are. If it does not mention something you find necessary, please do not hesitate to contact me. -== Using git / sending patches - -=== How to build? +== Building i3 You can build i3 like you build any other software package which uses autotools. Here’s a memory refresher: @@ -23,7 +21,7 @@ Here’s a memory refresher: (The autoreconf -fi step is unnecessary if you are building from a release tarball, but shouldn’t hurt either.) -==== Build system features +=== Build system features * We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building @@ -51,7 +49,7 @@ Here’s a memory refresher: * Coverage reports are now generated using “make check-code-coverage”, which requires specifying --enable-code-coverage when calling configure. -=== Introduction +== Using git / sending patches For a short introduction into using git, see http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy From bcaec96414de458c8185d9ea51ca7a051518e1a0 Mon Sep 17 00:00:00 2001 From: Orestis Date: Mon, 11 Sep 2017 22:31:29 +0300 Subject: [PATCH 030/187] Fix userguide bug (#2932) Fixes #2931 --- docs/userguide | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/userguide b/docs/userguide index 0d5de3b9..76982207 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1949,7 +1949,6 @@ bindsym $mod+x focus output HDMI-2 bindsym $mod+x focus output primary ------------------------------------------------- -------------------------------- Note that you might not have a primary output configured yet. To do so, run: ------------------------- xrandr --output --primary From 48fbb3e9ef4de347f3a51bb24a5365d217413e37 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 12 Sep 2017 22:16:36 +0200 Subject: [PATCH 031/187] testsuite: install Module::Install so that AnyEvent-I3/Makefile.PL works (#2940) As per https://perlmaven.com/cant-locate-inc-module-install-in-inc, the inc/ directory should not be under version control. fixes #2914 --- docs/testsuite | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/testsuite b/docs/testsuite index 4fa3e9e1..4f9cdbab 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -83,6 +83,7 @@ $ cd ~/i3/testcases $ sudo apt-get install cpanminus $ sudo cpanm . $ cd ~/i3/AnyEvent-I3 +$ sudo cpanm Module::Install $ sudo cpanm . -------------------------------------------------------------------------------- @@ -93,6 +94,7 @@ If you don’t want to use cpanminus for some reason, the same works with cpan: $ cd ~/i3/testcases $ sudo cpan . $ cd ~/i3/AnyEvent-I3 +$ sudo cpan Module::Install $ sudo cpan . -------------------------------------------------------------------------------- From 30b1ab38b39e4acf7587e04776b31d58577763df Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sat, 9 Sep 2017 07:18:29 +0000 Subject: [PATCH 032/187] Introduce output_primary_name function Currently simply returns output->name, but this will make it easier to change how output names are stored in the following commits. Also replace reading output->name with invocations of output_primary_name. Code which writes output->name is unchanged. Done using a mostly mechanical replacement of output->name to output_primary_name(output). --- include/output.h | 6 ++++++ src/con.c | 2 +- src/fake_outputs.c | 2 +- src/handlers.c | 2 +- src/ipc.c | 2 +- src/main.c | 2 +- src/manage.c | 2 +- src/output.c | 8 ++++++++ src/randr.c | 38 +++++++++++++++++++------------------- src/tree.c | 4 ++-- src/workspace.c | 4 ++-- src/xinerama.c | 2 +- 12 files changed, 44 insertions(+), 30 deletions(-) diff --git a/include/output.h b/include/output.h index b13c9728..31084da1 100644 --- a/include/output.h +++ b/include/output.h @@ -24,6 +24,12 @@ Con *output_get_content(Con *output); */ Output *get_output_from_string(Output *current_output, const char *output_str); +/** + * Retrieves the primary name of an output. + * + */ +char *output_primary_name(Output *output); + /** * Returns the output for the given con. * diff --git a/src/con.c b/src/con.c index b520c110..6bbe692f 100644 --- a/src/con.c +++ b/src/con.c @@ -1264,7 +1264,7 @@ void con_move_to_output(Con *con, Output *output) { Con *ws = NULL; GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); assert(ws != NULL); - DLOG("Moving con %p to output %s\n", con, output->name); + DLOG("Moving con %p to output %s\n", con, output_primary_name(output)); con_move_to_workspace(con, ws, false, false, false); } diff --git a/src/fake_outputs.c b/src/fake_outputs.c index b898ce98..93e00e72 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -49,7 +49,7 @@ void fake_outputs_init(const char *output_spec) { } else { new_output = scalloc(1, sizeof(Output)); sasprintf(&(new_output->name), "fake-%d", num_screens); - DLOG("Created new fake output %s (%p)\n", new_output->name, new_output); + DLOG("Created new fake output %s (%p)\n", output_primary_name(new_output), new_output); new_output->active = true; new_output->rect.x = x; new_output->rect.y = y; diff --git a/src/handlers.c b/src/handlers.c index c273e116..8d500fd9 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -388,7 +388,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { Con *current_output = con_get_output(con); Output *target = get_output_containing(x, y); if (target != NULL && current_output != target->con) { - DLOG("Dock client is requested to be moved to output %s, moving it there.\n", target->name); + DLOG("Dock client is requested to be moved to output %s, moving it there.\n", output_primary_name(target)); Match *match; Con *nc = con_for_window(target->con, con->window, &match); DLOG("Dock client will be moved to container %p.\n", nc); diff --git a/src/ipc.c b/src/ipc.c index 18d6075d..b02b3fe4 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -829,7 +829,7 @@ IPC_HANDLER(get_outputs) { y(map_open); ystr("name"); - ystr(output->name); + ystr(output_primary_name(output)); ystr("active"); y(bool, output->active); diff --git a/src/main.c b/src/main.c index 21265446..44e4517e 100644 --- a/src/main.c +++ b/src/main.c @@ -687,7 +687,7 @@ int main(int argc, char *argv[]) { TAILQ_FOREACH(con, &(croot->nodes_head), nodes) { Output *output; TAILQ_FOREACH(output, &outputs, outputs) { - if (output->active || strcmp(con->name, output->name) != 0) + if (output->active || strcmp(con->name, output_primary_name(output)) != 0) continue; /* This will correctly correlate the output with its content diff --git a/src/manage.c b/src/manage.c index 86a361c3..004e8038 100644 --- a/src/manage.c +++ b/src/manage.c @@ -219,7 +219,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki LOG("This window is of type dock\n"); Output *output = get_output_containing(geom->x, geom->y); if (output != NULL) { - DLOG("Starting search at output %s\n", output->name); + DLOG("Starting search at output %s\n", output_primary_name(output)); search_at = output->con; } diff --git a/src/output.c b/src/output.c index e3c54a67..1b232694 100644 --- a/src/output.c +++ b/src/output.c @@ -44,6 +44,14 @@ Output *get_output_from_string(Output *current_output, const char *output_str) { return get_output_by_name(output_str, true); } +/* + * Retrieves the primary name of an output. + * + */ +char *output_primary_name(Output *output) { + return output->name; +} + Output *get_output_for_con(Con *con) { Con *output_con = con_get_output(con); if (output_con == NULL) { diff --git a/src/randr.c b/src/randr.c index 48bffb46..9cb45c5d 100644 --- a/src/randr.c +++ b/src/randr.c @@ -49,7 +49,7 @@ Output *get_output_by_name(const char *name, const bool require_active) { bool get_primary = (strcasecmp("primary", name) == 0); TAILQ_FOREACH(output, &outputs, outputs) { if ((output->primary && get_primary) || - ((!require_active || output->active) && strcasecmp(output->name, name) == 0)) { + ((!require_active || output->active) && strcasecmp(output_primary_name(output), name) == 0)) { return output; } } @@ -178,7 +178,7 @@ Output *get_output_next_wrap(direction_t direction, Output *current) { } if (!best) best = current; - DLOG("current = %s, best = %s\n", current->name, best->name); + DLOG("current = %s, best = %s\n", output_primary_name(current), output_primary_name(best)); return best; } @@ -250,7 +250,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far } } - DLOG("current = %s, best = %s\n", current->name, (best ? best->name : "NULL")); + DLOG("current = %s, best = %s\n", output_primary_name(current), (best ? output_primary_name(best) : "NULL")); return best; } @@ -280,12 +280,12 @@ void output_init_con(Output *output) { Con *con = NULL, *current; bool reused = false; - DLOG("init_con for output %s\n", output->name); + DLOG("init_con for output %s\n", output_primary_name(output)); /* Search for a Con with that name directly below the root node. There * might be one from a restored layout. */ TAILQ_FOREACH(current, &(croot->nodes_head), nodes) { - if (strcmp(current->name, output->name) != 0) + if (strcmp(current->name, output_primary_name(output)) != 0) continue; con = current; @@ -297,7 +297,7 @@ void output_init_con(Output *output) { if (con == NULL) { con = con_new(croot, NULL); FREE(con->name); - con->name = sstrdup(output->name); + con->name = sstrdup(output_primary_name(output)); con->type = CT_OUTPUT; con->layout = L_OUTPUT; con_fix_percent(croot); @@ -384,7 +384,7 @@ void init_ws_for_output(Output *output, Con *content) { /* go through all assignments and move the existing workspaces to this output */ struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (strcmp(assignment->output, output->name) != 0) + if (strcmp(assignment->output, output_primary_name(output)) != 0) continue; /* check if this workspace actually exists */ @@ -402,13 +402,13 @@ void init_ws_for_output(Output *output, Con *content) { LOG("Workspace \"%s\" assigned to output \"%s\", but it is already " "there. Do you have two assignment directives for the same " "workspace in your configuration file?\n", - workspace->name, output->name); + workspace->name, output_primary_name(output)); continue; } /* if so, move it over */ LOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n", - workspace->name, workspace_out->name, output->name); + workspace->name, workspace_out->name, output_primary_name(output)); /* if the workspace is currently visible on that output, we need to * switch to a different workspace - otherwise the output would end up @@ -445,7 +445,7 @@ void init_ws_for_output(Output *output, Con *content) { workspace_out->name); init_ws_for_output(get_output_by_name(workspace_out->name, true), output_get_content(workspace_out)); - DLOG("Done re-initializing, continuing with \"%s\"\n", output->name); + DLOG("Done re-initializing, continuing with \"%s\"\n", output_primary_name(output)); } } @@ -465,7 +465,7 @@ void init_ws_for_output(Output *output, Con *content) { /* otherwise, we create the first assigned ws for this output */ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (strcmp(assignment->output, output->name) != 0) + if (strcmp(assignment->output, output_primary_name(output)) != 0) continue; LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n", @@ -652,7 +652,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, xcb_randr_get_output_info_name_length(output), xcb_randr_get_output_info_name(output)); - DLOG("found output with name %s\n", new->name); + DLOG("found output with name %s\n", output_primary_name(new)); /* Even if no CRTC is used at the moment, we store the output so that * we do not need to change the list ever again (we only update the @@ -672,7 +672,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts); if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL) { DLOG("Skipping output %s: could not get CRTC (%p)\n", - new->name, crtc); + output_primary_name(new), crtc); free(new); return; } @@ -789,7 +789,7 @@ void randr_query_outputs(void) { if (!output->active || output->to_be_disabled) continue; DLOG("output %p / %s, position (%d, %d), checking for clones\n", - output, output->name, output->rect.x, output->rect.y); + output, output_primary_name(output), output->rect.x, output->rect.y); for (other = output; other != TAILQ_END(&outputs); @@ -813,7 +813,7 @@ void randr_query_outputs(void) { update_if_necessary(&(other->rect.width), width); update_if_necessary(&(other->rect.height), height); - DLOG("disabling output %p (%s)\n", other, other->name); + DLOG("disabling output %p (%s)\n", other, output_primary_name(other)); other->to_be_disabled = true; DLOG("new output mode %d x %d, other mode %d x %d\n", @@ -828,7 +828,7 @@ void randr_query_outputs(void) { * LVDS1 gets disabled. */ TAILQ_FOREACH(output, &outputs, outputs) { if (output->active && output->con == NULL) { - DLOG("Need to initialize a Con for output %s\n", output->name); + DLOG("Need to initialize a Con for output %s\n", output_primary_name(output)); output_init_con(output); output->changed = false; } @@ -854,7 +854,7 @@ void randr_query_outputs(void) { Con *content = output_get_content(output->con); if (!TAILQ_EMPTY(&(content->nodes_head))) continue; - DLOG("Should add ws for output %s\n", output->name); + DLOG("Should add ws for output %s\n", output_primary_name(output)); init_ws_for_output(output, content); } @@ -863,7 +863,7 @@ void randr_query_outputs(void) { if (!output->primary || !output->con) continue; - DLOG("Focusing primary output %s\n", output->name); + DLOG("Focusing primary output %s\n", output_primary_name(output)); con_focus(con_descend_focused(output->con)); } @@ -881,7 +881,7 @@ void randr_disable_output(Output *output) { assert(output->to_be_disabled); output->active = false; - DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); + DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output)); Output *first = get_first_output(); diff --git a/src/tree.c b/src/tree.c index 2d4647f8..82a4756c 100644 --- a/src/tree.c +++ b/src/tree.c @@ -537,7 +537,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) if (!current_output) return false; - DLOG("Current output is %s\n", current_output->name); + DLOG("Current output is %s\n", output_primary_name(current_output)); /* Try to find next output */ direction_t direction; @@ -555,7 +555,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) next_output = get_output_next(direction, current_output, CLOSEST_OUTPUT); if (!next_output) return false; - DLOG("Next output is %s\n", next_output->name); + DLOG("Next output is %s\n", output_primary_name(next_output)); /* Find visible workspace on next output */ Con *workspace = NULL; diff --git a/src/workspace.c b/src/workspace.c index d7f2ce7c..4b350b82 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -188,7 +188,7 @@ Con *create_workspace_on_output(Output *output, Con *content) { struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->name, target_name) != 0 || - strcmp(assignment->output, output->name) == 0) + strcmp(assignment->output, output_primary_name(output)) == 0) continue; assigned = true; @@ -935,7 +935,7 @@ bool workspace_move_to_output(Con *ws, const char *name) { bool used_assignment = false; struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (assignment->output == NULL || strcmp(assignment->output, current_output->name) != 0) + if (assignment->output == NULL || strcmp(assignment->output, output_primary_name(current_output)) != 0) continue; /* check if this workspace is already attached to the tree */ diff --git a/src/xinerama.c b/src/xinerama.c index 25bfa6b1..9574b894 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -56,7 +56,7 @@ static void query_screens(xcb_connection_t *conn) { } else { s = scalloc(1, sizeof(Output)); sasprintf(&(s->name), "xinerama-%d", num_screens); - DLOG("Created new Xinerama screen %s (%p)\n", s->name, s); + DLOG("Created new Xinerama screen %s (%p)\n", output_primary_name(s), s); s->active = true; s->rect.x = screen_info[screen].x_org; s->rect.y = screen_info[screen].y_org; From d01a59b92246167975ca9bef41e223186a1c399c Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sat, 9 Sep 2017 07:37:37 +0000 Subject: [PATCH 033/187] Store output names as a linked list Currently, only one name is ever added, and only the first name is ever accessed; actually using the capability to store and access multiple names comes in the following commits. --- include/data.h | 14 ++++++++++++-- src/fake_outputs.c | 5 ++++- src/output.c | 2 +- src/randr.c | 28 +++++++++++++++++++++++----- src/xinerama.c | 5 ++++- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/include/data.h b/include/data.h index 69a79ade..31ef1dc1 100644 --- a/include/data.h +++ b/include/data.h @@ -349,6 +349,13 @@ struct Autostart { autostarts_always; }; +struct output_name { + char *name; + + SLIST_ENTRY(output_name) + names; +}; + /** * An Output is a physical output on your graphics driver. Outputs which * are currently in use have (output->active == true). Each output has a @@ -370,8 +377,11 @@ struct xoutput { bool to_be_disabled; bool primary; - /** Name of the output */ - char *name; + /** List of names for the output. + * An output always has at least one name; the first name is + * considered the primary one. */ + SLIST_HEAD(names_head, output_name) + names_head; /** Pointer to the Con which represents this output */ Con *con; diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 93e00e72..6639b361 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -47,8 +47,11 @@ void fake_outputs_init(const char *output_spec) { new_output->rect.width = min(new_output->rect.width, width); new_output->rect.height = min(new_output->rect.height, height); } else { + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); new_output = scalloc(1, sizeof(Output)); - sasprintf(&(new_output->name), "fake-%d", num_screens); + sasprintf(&(output_name->name), "fake-%d", num_screens); + SLIST_INIT(&(new_output->names_head)); + SLIST_INSERT_HEAD(&(new_output->names_head), output_name, names); DLOG("Created new fake output %s (%p)\n", output_primary_name(new_output), new_output); new_output->active = true; new_output->rect.x = x; diff --git a/src/output.c b/src/output.c index 1b232694..e7690384 100644 --- a/src/output.c +++ b/src/output.c @@ -49,7 +49,7 @@ Output *get_output_from_string(Output *current_output, const char *output_str) { * */ char *output_primary_name(Output *output) { - return output->name; + return SLIST_FIRST(&output->names_head)->name; } Output *get_output_for_con(Con *con) { diff --git a/src/randr.c b/src/randr.c index 9cb45c5d..df63826d 100644 --- a/src/randr.c +++ b/src/randr.c @@ -266,7 +266,11 @@ Output *create_root_output(xcb_connection_t *conn) { s->rect.y = 0; s->rect.width = root_screen->width_in_pixels; s->rect.height = root_screen->height_in_pixels; - s->name = "xroot-0"; + + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); + output_name->name = "xroot-0"; + SLIST_INIT(&s->names_head); + SLIST_INSERT_HEAD(&s->names_head, output_name, names); return s; } @@ -594,7 +598,12 @@ static bool randr_query_outputs_15(void) { Output *new = get_output_by_name(name, false); if (new == NULL) { new = scalloc(1, sizeof(Output)); - new->name = sstrdup(name); + + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); + output_name->name = sstrdup(name); + SLIST_INIT(&new->names_head); + SLIST_INSERT_HEAD(&new->names_head, output_name, names); + if (monitor_info->primary) { TAILQ_INSERT_HEAD(&outputs, new, outputs); } else { @@ -643,14 +652,23 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, Output *new = get_output_by_id(id); bool existing = (new != NULL); - if (!existing) + if (!existing) { new = scalloc(1, sizeof(Output)); + SLIST_INIT(&new->names_head); + } new->id = id; new->primary = (primary && primary->output == id); - FREE(new->name); - sasprintf(&new->name, "%.*s", + while (!SLIST_EMPTY(&new->names_head)) { + FREE(SLIST_FIRST(&new->names_head)->name); + struct output_name *old_head = SLIST_FIRST(&new->names_head); + SLIST_REMOVE_HEAD(&new->names_head, names); + FREE(old_head); + } + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); + sasprintf(&output_name->name, "%.*s", xcb_randr_get_output_info_name_length(output), xcb_randr_get_output_info_name(output)); + SLIST_INSERT_HEAD(&new->names_head, output_name, names); DLOG("found output with name %s\n", output_primary_name(new)); diff --git a/src/xinerama.c b/src/xinerama.c index 9574b894..d0651a85 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -55,7 +55,10 @@ static void query_screens(xcb_connection_t *conn) { s->rect.height = min(s->rect.height, screen_info[screen].height); } else { s = scalloc(1, sizeof(Output)); - sasprintf(&(s->name), "xinerama-%d", num_screens); + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); + sasprintf(&output_name->name, "xinerama-%d", num_screens); + SLIST_INIT(&s->names_head); + SLIST_INSERT_HEAD(&s->names_head, output_name, names); DLOG("Created new Xinerama screen %s (%p)\n", output_primary_name(s), s); s->active = true; s->rect.x = screen_info[screen].x_org; From 8d51780d13576241644ece73afa8e167b3c310f7 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sat, 9 Sep 2017 09:00:22 +0000 Subject: [PATCH 034/187] randr: Register monitors' output names as additional i3 output names In addition to the name of the monitor itself (which is still used as the i3 output's primary name), register RandR output names associated with the RandR monitor as alternative i3 output names. --- src/randr.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/randr.c b/src/randr.c index df63826d..92d652b7 100644 --- a/src/randr.c +++ b/src/randr.c @@ -599,9 +599,39 @@ static bool randr_query_outputs_15(void) { if (new == NULL) { new = scalloc(1, sizeof(Output)); + SLIST_INIT(&new->names_head); + + /* Register associated output names in addition to the monitor name */ + xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info); + int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info); + for (int i = 0; i < randr_output_len; i++) { + xcb_randr_output_t randr_output = randr_outputs[i]; + + xcb_randr_get_output_info_reply_t *info = + xcb_randr_get_output_info_reply(conn, + xcb_randr_get_output_info(conn, randr_output, monitors->timestamp), + NULL); + + if (info != NULL && info->crtc != XCB_NONE) { + char *oname; + sasprintf(&oname, "%.*s", + xcb_randr_get_output_info_name_length(info), + xcb_randr_get_output_info_name(info)); + + if (strcmp(name, oname) != 0) { + struct output_name *output_name = scalloc(1, sizeof(struct output_name)); + output_name->name = sstrdup(oname); + SLIST_INSERT_HEAD(&new->names_head, output_name, names); + } else { + free(oname); + } + } + FREE(info); + } + + /* Insert the monitor name last, so that it's used as the primary name */ struct output_name *output_name = scalloc(1, sizeof(struct output_name)); output_name->name = sstrdup(name); - SLIST_INIT(&new->names_head); SLIST_INSERT_HEAD(&new->names_head, output_name, names); if (monitor_info->primary) { From ae8f3c267990e2cbca84949ca17ded58302c2751 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sat, 9 Sep 2017 09:23:50 +0000 Subject: [PATCH 035/187] randr: Look up alternative output names when searching outputs Update get_output_by_name to look at all additional names added by the change in the previous commit, not just the primary one. --- src/randr.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/randr.c b/src/randr.c index 92d652b7..bc791696 100644 --- a/src/randr.c +++ b/src/randr.c @@ -48,10 +48,18 @@ Output *get_output_by_name(const char *name, const bool require_active) { Output *output; bool get_primary = (strcasecmp("primary", name) == 0); TAILQ_FOREACH(output, &outputs, outputs) { - if ((output->primary && get_primary) || - ((!require_active || output->active) && strcasecmp(output_primary_name(output), name) == 0)) { + if (output->primary && get_primary) { return output; } + if (require_active && !output->active) { + continue; + } + struct output_name *output_name; + SLIST_FOREACH(output_name, &output->names_head, names) { + if (strcasecmp(output_name->name, name) == 0) { + return output; + } + } } return NULL; From 6de9cdd96fe89c63d874b13a52ac2acfb3907dd1 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 11 Sep 2017 11:15:56 +0000 Subject: [PATCH 036/187] ipc: Canonicalize output names in bar configuration Convert the output names specified in the "output" and "tray_output" fields in bar blocks in i3's configuration to the referred output's primary name. This allows specifying names other than the primary output's name in the given fields without changing the IPC protocol. --- src/ipc.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ipc.c b/src/ipc.c index b02b3fe4..274f6010 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -579,6 +579,11 @@ static void dump_bar_bindings(yajl_gen gen, Barconfig *config) { y(array_close); } +static char *canonicalize_output_name(char *name) { + Output *output = get_output_by_name(name, false); + return output ? output_primary_name(output) : name; +} + static void dump_bar_config(yajl_gen gen, Barconfig *config) { y(map_open); @@ -588,8 +593,13 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { if (config->num_outputs > 0) { ystr("outputs"); y(array_open); - for (int c = 0; c < config->num_outputs; c++) - ystr(config->outputs[c]); + for (int c = 0; c < config->num_outputs; c++) { + /* Convert monitor names (RandR ≥ 1.5) or output names + * (RandR < 1.5) into monitor names. This way, existing + * configs which use output names transparently keep + * working. */ + ystr(canonicalize_output_name(config->outputs[c])); + } y(array_close); } @@ -599,7 +609,7 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { struct tray_output_t *tray_output; TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) { - ystr(tray_output->output); + ystr(canonicalize_output_name(tray_output->output)); } y(array_close); From dfc5ad98504af0d1480b171031feb41c1bf3ed83 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 06:58:29 +0000 Subject: [PATCH 037/187] inject_randr1.5: Refactor reading and storing reply buffer to a struct Allows easier introduction of additional reply buffers in upcoming changes. --- testcases/inject_randr1.5.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/testcases/inject_randr1.5.c b/testcases/inject_randr1.5.c index 5796ef05..b09510d0 100644 --- a/testcases/inject_randr1.5.c +++ b/testcases/inject_randr1.5.c @@ -41,9 +41,13 @@ void cleanup_socket(void) { } } +struct injected_reply { + void *buf; + off_t len; +}; + /* BEGIN RandR 1.5 specific */ -static void *injected_reply = NULL; -static off_t injected_reply_len = 0; +static struct injected_reply getmonitors_reply = {NULL, 0}; /* END RandR 1.5 specific */ #define XCB_PAD(i) (-(i)&3) @@ -294,10 +298,10 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) { if (sequence == connstate->getmonitors) { printf("RRGetMonitors reply!\n"); - if (injected_reply != NULL) { + if (getmonitors_reply.buf != NULL) { printf("injecting reply\n"); - ((generic_x11_reply_t *)injected_reply)->sequence = sequence; - must_write(writeall(connstate->clientw->fd, injected_reply, injected_reply_len)); + ((generic_x11_reply_t *)getmonitors_reply.buf)->sequence = sequence; + must_write(writeall(connstate->clientw->fd, getmonitors_reply.buf, getmonitors_reply.len)); free(packet); return; } @@ -322,7 +326,7 @@ static void child_cb(EV_P_ ev_child *w, int revents) { } } -static void must_read_reply(const char *filename) { +static void must_read_reply(const char *filename, struct injected_reply *reply) { FILE *f; if ((f = fopen(filename, "r")) == NULL) { err(EXIT_FAILURE, "fopen(%s)", filename); @@ -331,11 +335,9 @@ static void must_read_reply(const char *filename) { if (fstat(fileno(f), &stbuf) != 0) { err(EXIT_FAILURE, "fstat(%s)", filename); } - /* BEGIN RandR 1.5 specific */ - injected_reply_len = stbuf.st_size; - injected_reply = smalloc(stbuf.st_size); - int n = fread(injected_reply, 1, stbuf.st_size, f); - /* END RandR 1.5 specific */ + reply->len = stbuf.st_size; + reply->buf = smalloc(stbuf.st_size); + int n = fread(reply->buf, 1, stbuf.st_size, f); if (n != stbuf.st_size) { err(EXIT_FAILURE, "fread(%s)", filename); } @@ -355,7 +357,7 @@ int main(int argc, char *argv[]) { switch (opt) { case 0: if (strcmp(long_options[option_index].name, "getmonitors_reply") == 0) { - must_read_reply(optarg); + must_read_reply(optarg, &getmonitors_reply); } break; default: From f88f4fb5d41de8bea15ffefed68462f5da78ba63 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 07:03:20 +0000 Subject: [PATCH 038/187] inject_randr1.5: Add RRGetOutputInfo reply injection Add a --getoutputinfo_reply switch to indicate a filename containing the RRGetOutputInfo reply data to inject. --- testcases/inject_randr1.5.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/testcases/inject_randr1.5.c b/testcases/inject_randr1.5.c index b09510d0..955df1e5 100644 --- a/testcases/inject_randr1.5.c +++ b/testcases/inject_randr1.5.c @@ -48,6 +48,7 @@ struct injected_reply { /* BEGIN RandR 1.5 specific */ static struct injected_reply getmonitors_reply = {NULL, 0}; +static struct injected_reply getoutputinfo_reply = {NULL, 0}; /* END RandR 1.5 specific */ #define XCB_PAD(i) (-(i)&3) @@ -70,6 +71,8 @@ struct connstate { int getext_randr; /* sequence number of the most recent RRGetMonitors request */ int getmonitors; + /* sequence number of the most recent RRGetOutputInfo request */ + int getoutputinfo; int randr_major_opcode; /* END RandR 1.5 specific */ @@ -263,6 +266,8 @@ static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) { const uint8_t randr_opcode = ((generic_x11_request_t *)request)->pad0; if (randr_opcode == XCB_RANDR_GET_MONITORS) { connstate->getmonitors = connstate->sequence; + } else if (randr_opcode == XCB_RANDR_GET_OUTPUT_INFO) { + connstate->getoutputinfo = connstate->sequence; } } /* END RandR 1.5 specific */ @@ -306,6 +311,17 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) { return; } } + + if (sequence == connstate->getoutputinfo) { + printf("RRGetOutputInfo reply!\n"); + if (getoutputinfo_reply.buf != NULL) { + printf("injecting reply\n"); + ((generic_x11_reply_t *)getoutputinfo_reply.buf)->sequence = sequence; + must_write(writeall(connstate->clientw->fd, getoutputinfo_reply.buf, getoutputinfo_reply.len)); + free(packet); + return; + } + } /* END RandR 1.5 specific */ break; @@ -347,6 +363,7 @@ static void must_read_reply(const char *filename, struct injected_reply *reply) int main(int argc, char *argv[]) { static struct option long_options[] = { {"getmonitors_reply", required_argument, 0, 0}, + {"getoutputinfo_reply", required_argument, 0, 0}, {0, 0, 0, 0}, }; char *options_string = ""; @@ -355,11 +372,15 @@ int main(int argc, char *argv[]) { while ((opt = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { switch (opt) { - case 0: - if (strcmp(long_options[option_index].name, "getmonitors_reply") == 0) { + case 0: { + const char *option_name = long_options[option_index].name; + if (strcmp(option_name, "getmonitors_reply") == 0) { must_read_reply(optarg, &getmonitors_reply); + } else if (strcmp(option_name, "getoutputinfo_reply") == 0) { + must_read_reply(optarg, &getoutputinfo_reply); } break; + } default: exit(EXIT_FAILURE); } From de1b578f75c79e74cb5d244e4be90c53936d67ef Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 08:39:15 +0000 Subject: [PATCH 039/187] inject_randr1.5: Intercept X11 error responses in addition to replies Allow clients to send garbage to the server, then intercept the server's error response and substitute it with the supplied simulated reply data. --- testcases/inject_randr1.5.c | 62 +++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/testcases/inject_randr1.5.c b/testcases/inject_randr1.5.c index 955df1e5..6cccfa76 100644 --- a/testcases/inject_randr1.5.c +++ b/testcases/inject_randr1.5.c @@ -276,6 +276,32 @@ static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) { free(request); } +static bool handle_sequence(struct connstate *connstate, uint16_t sequence) { + /* BEGIN RandR 1.5 specific */ + if (sequence == connstate->getmonitors) { + printf("RRGetMonitors reply!\n"); + if (getmonitors_reply.buf != NULL) { + printf("injecting reply\n"); + ((generic_x11_reply_t *)getmonitors_reply.buf)->sequence = sequence; + must_write(writeall(connstate->clientw->fd, getmonitors_reply.buf, getmonitors_reply.len)); + return true; + } + } + + if (sequence == connstate->getoutputinfo) { + printf("RRGetOutputInfo reply!\n"); + if (getoutputinfo_reply.buf != NULL) { + printf("injecting reply\n"); + ((generic_x11_reply_t *)getoutputinfo_reply.buf)->sequence = sequence; + must_write(writeall(connstate->clientw->fd, getoutputinfo_reply.buf, getoutputinfo_reply.len)); + return true; + } + } + /* END RandR 1.5 specific */ + + return false; +} + static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) { struct connstate *connstate = (struct connstate *)w->data; // all packets from the server are at least 32 bytes in length @@ -283,9 +309,14 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) { void *packet = smalloc(len); must_read(readall_into(packet, len, connstate->serverw->fd)); switch (((generic_x11_reply_t *)packet)->code) { - case 0: // error + case 0: { // error + const uint16_t sequence = ((xcb_request_error_t *)packet)->sequence; + if (handle_sequence(connstate, sequence)) { + free(packet); + return; + } break; - + } case 1: // reply len += ((generic_x11_reply_t *)packet)->length * 4; if (len > 32) { @@ -300,30 +331,13 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) { xcb_query_extension_reply_t *reply = packet; connstate->randr_major_opcode = reply->major_opcode; } - - if (sequence == connstate->getmonitors) { - printf("RRGetMonitors reply!\n"); - if (getmonitors_reply.buf != NULL) { - printf("injecting reply\n"); - ((generic_x11_reply_t *)getmonitors_reply.buf)->sequence = sequence; - must_write(writeall(connstate->clientw->fd, getmonitors_reply.buf, getmonitors_reply.len)); - free(packet); - return; - } - } - - if (sequence == connstate->getoutputinfo) { - printf("RRGetOutputInfo reply!\n"); - if (getoutputinfo_reply.buf != NULL) { - printf("injecting reply\n"); - ((generic_x11_reply_t *)getoutputinfo_reply.buf)->sequence = sequence; - must_write(writeall(connstate->clientw->fd, getoutputinfo_reply.buf, getoutputinfo_reply.len)); - free(packet); - return; - } - } /* END RandR 1.5 specific */ + if (handle_sequence(connstate, sequence)) { + free(packet); + return; + } + break; default: // event From c6e3fbcf1915779838e8ea06291194b0b2421de9 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 08:41:00 +0000 Subject: [PATCH 040/187] testcases/lib: Add inject_randr15_outputinfo argument Allow tests to specify a file name for inject_randr15's --getoutputinfo_reply command-line parameter. --- testcases/lib/SocketActivation.pm | 7 ++++++- testcases/lib/i3test.pm.in | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm index 0f307eb3..5951fd26 100644 --- a/testcases/lib/SocketActivation.pm +++ b/testcases/lib/SocketActivation.pm @@ -145,7 +145,12 @@ sub activate_i3 { if ($args{inject_randr15}) { # See comment in $args{strace} branch. $cmd = 'test.inject_randr15 --getmonitors_reply="' . - $args{inject_randr15} . '" -- ' . + $args{inject_randr15} . '" ' . + ($args{inject_randr15_outputinfo} + ? '--getoutputinfo_reply="' . + $args{inject_randr15_outputinfo} . '" ' + : '') . + '-- ' . 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"'; } diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index 4046e620..683f3d39 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -864,6 +864,7 @@ sub launch_with_config { dont_create_temp_dir => $args{dont_create_temp_dir}, validate_config => $args{validate_config}, inject_randr15 => $args{inject_randr15}, + inject_randr15_outputinfo => $args{inject_randr15_outputinfo}, ); # If we called i3 with -C, we wait for it to exit and then return as From 8a0e5f89f92dca43a1b295d380305f5b9c083684 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 08:42:46 +0000 Subject: [PATCH 041/187] 533-randr15.t: Add a fake output connected to the fake monitor Add an output ID to the simulated RRGetMonitors reply, then add a simulated RRGetOutputInfo reply describing the added output. --- testcases/t/533-randr15.t | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/testcases/t/533-randr15.t b/testcases/t/533-randr15.t index 08fa88cc..da51a097 100644 --- a/testcases/t/533-randr15.t +++ b/testcases/t/533-randr15.t @@ -33,12 +33,12 @@ my ($outfh, $outname) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1); my $reply = pack('cxSLLLLx[LLL]', 1, # reply 0, # sequence (will be filled in by inject_randr15) - # 56 = length($reply) + length($monitor1) + # 60 = length($reply) + length($monitor1) # 32 = minimum X11 reply length - (56-32) / 4, # length in words + (60-32) / 4, # length in words 0, # timestamp TODO 1, # nmonitors - 0); # noutputs + 1); # noutputs # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if # they are not yet interned. @@ -47,24 +47,45 @@ my $DP3 = $x->intern_atom_reply($atom_cookie->{sequence})->{atom}; # MONITORINFO is defined in A.1.1 in # https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt -my $monitor1 = pack('LccSssSSLL', +my $monitor1 = pack('LccSssSSLLL', $DP3, # name (ATOM) 1, # primary 1, # automatic - 0, # ncrtcs + 1, # ncrtcs 0, # x 0, # y 3840, # width in pixels 2160, # height in pixels 520, # width in millimeters - 290); # height in millimeters + 290, # height in millimeters + 12345); # output ID #0 print $outfh $reply; print $outfh $monitor1; close($outfh); -my $pid = launch_with_config($config, inject_randr15 => $outname); +# Prepare a RRGetOutputInfo reply as well; see RRGetOutputInfo in +# https://www.x.org/releases/current/doc/randrproto/randrproto.txt +my $output_name = 'i3-fake-output'; +($outfh, my $outname_moninfo) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1); +my $moninfo = pack('cxSLLLx[LLccSSSS]S a* x!4', + 1, # reply + 0, # sequence (will be filled in by inject_randr15) + # 36 = length($moninfo) (without name and padding) + # 32 = minimum X11 reply length + ((36 + length($output_name) - 32) + 3) / 4, # length in words + 0, # timestamp TODO + 12345, # CRTC + length($output_name), # length of name + $output_name); # name + +print $outfh $moninfo; +close($outfh); + +my $pid = launch_with_config($config, + inject_randr15 => $outname, + inject_randr15_outputinfo => $outname_moninfo); my $tree = i3->get_tree->recv; my @outputs = map { $_->{name} } @{$tree->{nodes}}; @@ -86,7 +107,7 @@ exit_gracefully($pid); # When inject_randr15 is defined but false, fake-xinerama will be turned off, # but inject_randr15 will not actually be used. -my $pid = launch_with_config($config, inject_randr15 => ''); +$pid = launch_with_config($config, inject_randr15 => ''); $tree = i3->get_tree->recv; @outputs = map { $_->{name} } @{$tree->{nodes}}; From fbeded23a9d82ec431a0f03aa7fbdc45d07d6199 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 08:50:40 +0000 Subject: [PATCH 042/187] 533-randr15.t: Stop hard-coding the output name Refactor away all mentions of DP3. --- testcases/t/533-randr15.t | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/testcases/t/533-randr15.t b/testcases/t/533-randr15.t index da51a097..e22e8116 100644 --- a/testcases/t/533-randr15.t +++ b/testcases/t/533-randr15.t @@ -42,13 +42,14 @@ my $reply = pack('cxSLLLLx[LLL]', # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if # they are not yet interned. -my $atom_cookie = $x->intern_atom(0, length("DP3"), "DP3"); -my $DP3 = $x->intern_atom_reply($atom_cookie->{sequence})->{atom}; +my $monitor_name = 'i3-fake-monitor'; +my $atom_cookie = $x->intern_atom(0, length($monitor_name), $monitor_name); +my $monitor_name_atom = $x->intern_atom_reply($atom_cookie->{sequence})->{atom}; # MONITORINFO is defined in A.1.1 in # https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt my $monitor1 = pack('LccSssSSLLL', - $DP3, # name (ATOM) + $monitor_name_atom, # name (ATOM) 1, # primary 1, # automatic 1, # ncrtcs @@ -89,15 +90,15 @@ my $pid = launch_with_config($config, my $tree = i3->get_tree->recv; my @outputs = map { $_->{name} } @{$tree->{nodes}}; -is_deeply(\@outputs, [ '__i3', 'DP3' ], 'outputs are __i3 and DP3'); +is_deeply(\@outputs, [ '__i3', $monitor_name ], 'outputs are __i3 and the fake monitor'); -my ($dp3) = grep { $_->{name} eq 'DP3' } @{$tree->{nodes}}; -is_deeply($dp3->{rect}, { +my ($output_data) = grep { $_->{name} eq $monitor_name } @{$tree->{nodes}}; +is_deeply($output_data->{rect}, { width => 3840, height => 2160, x => 0, y => 0, - }, 'Output DP3 at 3840x2160+0+0'); + }, "Fake output at 3840x2160+0+0"); exit_gracefully($pid); From 9be4199e13433c5c0b9e61f8cc962623436158c4 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 09:18:40 +0000 Subject: [PATCH 043/187] 533-randr15.t: Add test for bar output name canonicalization --- testcases/t/533-randr15.t | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/testcases/t/533-randr15.t b/testcases/t/533-randr15.t index e22e8116..5b81194a 100644 --- a/testcases/t/533-randr15.t +++ b/testcases/t/533-randr15.t @@ -20,10 +20,16 @@ use File::Temp qw(tempfile); use i3test i3_autostart => 0; +my $monitor_name = 'i3-fake-monitor'; +my $output_name = 'i3-fake-output'; + my $config = < 1); @@ -42,7 +48,6 @@ my $reply = pack('cxSLLLLx[LLL]', # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if # they are not yet interned. -my $monitor_name = 'i3-fake-monitor'; my $atom_cookie = $x->intern_atom(0, length($monitor_name), $monitor_name); my $monitor_name_atom = $x->intern_atom_reply($atom_cookie->{sequence})->{atom}; @@ -68,7 +73,6 @@ close($outfh); # Prepare a RRGetOutputInfo reply as well; see RRGetOutputInfo in # https://www.x.org/releases/current/doc/randrproto/randrproto.txt -my $output_name = 'i3-fake-output'; ($outfh, my $outname_moninfo) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1); my $moninfo = pack('cxSLLLx[LLccSSSS]S a* x!4', 1, # reply @@ -100,6 +104,17 @@ is_deeply($output_data->{rect}, { y => 0, }, "Fake output at 3840x2160+0+0"); +# Verify that i3 canonicalizes RandR output names to i3 output names +# (RandR monitor names) for bar configs + +my $bars = i3->get_bar_config()->recv; +is(@$bars, 1, 'one bar configured'); + +my $bar_id = shift @$bars; + +my $bar_config = i3->get_bar_config($bar_id)->recv; +is_deeply($bar_config->{outputs}, [ $monitor_name ], 'bar_config output name is normalized'); + exit_gracefully($pid); ################################################################################ From 3b75be1334eab7a2c3513cb0e52969480b97199d Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 12 Sep 2017 09:27:49 +0000 Subject: [PATCH 044/187] docs/userguide: Document that i3 can accept RandR output names --- docs/userguide | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/userguide b/docs/userguide index 0d5de3b9..200aabb8 100644 --- a/docs/userguide +++ b/docs/userguide @@ -869,6 +869,18 @@ The 'output' is the name of the RandR output you attach your screen to. On a laptop, you might have VGA1 and LVDS1 as output names. You can see the available outputs by running +xrandr --current+. +If your X server supports RandR 1.5 or newer, i3 will use RandR monitor objects +instead of output objects. Run +xrandr --listmonitors+ to see a list. Usually, +a monitor object contains exactly one output, and has the same name as the +output; but should that not be the case, you may specify the name of either the +monitor or the output in i3's configuration. For example, the Dell UP2414Q uses +two scalers internally, so its output names might be “DP1” and “DP2”, but the +monitor name is “Dell UP2414Q”. + +(Note that even if you specify the name of an output which doesn't span the +entire monitor, i3 will still use the entire area of the containing monitor +rather than that of just the output's.) + If you use named workspaces, they must be quoted: *Examples*: From fd7319683d8c4a5efbfd3431c1ab22ecac8bf59d Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Wed, 13 Sep 2017 13:32:12 +0300 Subject: [PATCH 045/187] Don't insert newline at end of config with launch_with_config --- testcases/lib/i3test.pm.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index 683f3d39..5e3f8b2d 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -835,8 +835,11 @@ sub launch_with_config { my ($fh, $tmpfile) = tempfile("i3-cfg-for-$ENV{TESTNAME}-XXXXX", UNLINK => 1); + say $fh "ipc-socket $tmp_socket_path" + unless $args{dont_add_socket_path}; + if ($config ne '-default') { - say $fh $config; + print $fh $config; } else { open(my $conf_fh, '<', '@abs_top_srcdir@/testcases/i3-test.config') or $tester->BAIL_OUT("could not open default config: $!"); @@ -844,9 +847,6 @@ sub launch_with_config { say $fh scalar <$conf_fh>; } - say $fh "ipc-socket $tmp_socket_path" - unless $args{dont_add_socket_path}; - close($fh); my $cv = AnyEvent->condvar; From 7b0f4abf4faa15ba6cc71a59cf9f57ef682fe596 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Tue, 12 Sep 2017 12:29:01 +0300 Subject: [PATCH 046/187] scalloc parse_config input to make sure it terminates with '\0' Otherwise strchr() can crash for files that don't end with '\n' because it won't find a null char to terminate at. Fixes #2934 --- src/config_parser.c | 2 +- testcases/t/270-config-no-newline-end.t | 41 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 testcases/t/270-config-no-newline-end.t diff --git a/src/config_parser.c b/src/config_parser.c index c88e9d1e..58a5552c 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -1025,7 +1025,7 @@ bool parse_file(const char *f, bool use_nagbar) { /* Then, allocate a new buffer and copy the file over to the new one, * but replace occurrences of our variables */ char *walk = buf, *destwalk; - char *new = smalloc(stbuf.st_size + extra_bytes + 1); + char *new = scalloc(stbuf.st_size + extra_bytes + 1, 1); destwalk = new; while (walk < (buf + stbuf.st_size)) { /* Find the next variable */ diff --git a/testcases/t/270-config-no-newline-end.t b/testcases/t/270-config-no-newline-end.t new file mode 100644 index 00000000..4e7ccbf7 --- /dev/null +++ b/testcases/t/270-config-no-newline-end.t @@ -0,0 +1,41 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Make sure that configs that end without a newline don't crash i3. +# Ticket: #2934 +use i3test i3_autostart => 0; + +my $first_lines = <<'EOT'; +set $workspace1 workspace number 1 +set $workspace0 workspace eggs + +bindsym Mod4+1 $workspace1 +EOT + +# Intentionally don't add a trailing newline for the last line since this is +# what triggered the bug. +my $last_line = 'bindsym Mod4+0 $workspace0'; +my $config = "${first_lines}${last_line}"; + +my $pid = launch_with_config($config); +does_i3_live; + +my $i3 = i3(get_socket_path()); +my $ws = $i3->get_workspaces->recv; +is($ws->[0]->{name}, 'eggs', 'last line processed correctly'); + +exit_gracefully($pid); +done_testing; From b23e1f644f78199508d9a7295efbf609e8d26184 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 13 Sep 2017 16:39:13 +0200 Subject: [PATCH 047/187] Bugfix: free incomplete containers when JSON parsing fails related to #2755 --- include/con.h | 6 ++ src/con.c | 24 +++++++ src/load_layout.c | 13 ++++ src/tree.c | 17 +---- testcases/t/215-layout-restore-crash.t | 93 +++++++++++++++++++++++++- 5 files changed, 135 insertions(+), 18 deletions(-) diff --git a/include/con.h b/include/con.h index 1c7bb932..6a7e31bc 100644 --- a/include/con.h +++ b/include/con.h @@ -25,6 +25,12 @@ Con *con_new_skeleton(Con *parent, i3Window *window); */ Con *con_new(Con *parent, i3Window *window); +/** + * Frees the specified container. + * + */ +void con_free(Con *con); + /** * Sets input focus to the given container. Will be updated in X11 in the next * run of x_push_changes(). diff --git a/src/con.c b/src/con.c index 6bbe692f..9797afa6 100644 --- a/src/con.c +++ b/src/con.c @@ -73,6 +73,30 @@ Con *con_new(Con *parent, i3Window *window) { return new; } +/* + * Frees the specified container. + * + */ +void con_free(Con *con) { + free(con->name); + FREE(con->deco_render_params); + TAILQ_REMOVE(&all_cons, con, all_cons); + while (!TAILQ_EMPTY(&(con->swallow_head))) { + Match *match = TAILQ_FIRST(&(con->swallow_head)); + TAILQ_REMOVE(&(con->swallow_head), match, matches); + match_free(match); + free(match); + } + while (!TAILQ_EMPTY(&(con->marks_head))) { + mark_t *mark = TAILQ_FIRST(&(con->marks_head)); + TAILQ_REMOVE(&(con->marks_head), mark, marks); + FREE(mark->name); + FREE(mark); + } + free(con); + DLOG("con %p freed\n", con); +} + static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) { con->parent = parent; Con *loop; diff --git a/src/load_layout.c b/src/load_layout.c index 7961e17f..0fa3e85b 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -18,6 +18,7 @@ /* TODO: refactor the whole parsing thing */ static char *last_key; +static int incomplete; static Con *json_node; static Con *to_focus; static bool parsing_swallows; @@ -68,6 +69,9 @@ static int json_start_map(void *ctx) { json_node->name = NULL; json_node->parent = parent; } + /* json_node is incomplete and should be removed if parsing fails */ + incomplete++; + DLOG("incomplete = %d\n", incomplete); } } return 1; @@ -166,6 +170,8 @@ static int json_end_map(void *ctx) { LOG("Creating window\n"); x_con_init(json_node); json_node = json_node->parent; + incomplete--; + DLOG("incomplete = %d\n", incomplete); } if (parsing_swallows && swallow_is_empty) { @@ -634,6 +640,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { yajl_status stat; json_node = con; to_focus = NULL; + incomplete = 0; parsing_swallows = false; parsing_rect = false; parsing_deco_rect = false; @@ -649,6 +656,12 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { if (errormsg != NULL) *errormsg = sstrdup((const char *)str); yajl_free_error(hand, str); + while (incomplete-- > 0) { + Con *parent = json_node->parent; + DLOG("freeing incomplete container %p\n", json_node); + con_free(json_node); + json_node = parent; + } } /* In case not all containers were restored, we need to fix the diff --git a/src/tree.c b/src/tree.c index 82a4756c..296a6a37 100644 --- a/src/tree.c +++ b/src/tree.c @@ -320,22 +320,7 @@ bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_par DLOG("parent container killed\n"); } - free(con->name); - FREE(con->deco_render_params); - TAILQ_REMOVE(&all_cons, con, all_cons); - while (!TAILQ_EMPTY(&(con->swallow_head))) { - Match *match = TAILQ_FIRST(&(con->swallow_head)); - TAILQ_REMOVE(&(con->swallow_head), match, matches); - match_free(match); - free(match); - } - while (!TAILQ_EMPTY(&(con->marks_head))) { - mark_t *mark = TAILQ_FIRST(&(con->marks_head)); - TAILQ_REMOVE(&(con->marks_head), mark, marks); - FREE(mark->name); - FREE(mark); - } - free(con); + con_free(con); /* in the case of floating windows, we already focused another container * when closing the parent, so we can exit now. */ diff --git a/testcases/t/215-layout-restore-crash.t b/testcases/t/215-layout-restore-crash.t index 4430dac8..5b34c29c 100644 --- a/testcases/t/215-layout-restore-crash.t +++ b/testcases/t/215-layout-restore-crash.t @@ -131,14 +131,103 @@ print $fh <<'EOT'; EOT $fh->flush; my $reply = cmd "append_layout $filename"; -diag('reply = ' . Dumper($reply)); +ok(!$reply->[0]->{success}, 'IPC reply did not indicate success'); does_i3_live; -ok(!$reply->[0]->{success}, 'IPC reply did not indicate success'); close($fh); +################################################################################ +# another file with a superfluous trailing comma (issue #2755) +################################################################################ + +subtest 'issue 2755' => sub { + plan tests => 4; + $ws = fresh_workspace; + + @content = @{get_ws_content($ws)}; + is(@content, 0, 'no nodes on the new workspace yet'); + + ($fh, $filename) = tempfile(UNLINK => 1); + print $fh <<'EOT'; +// vim:ts=4:sw=4:et +{ + // splith split container with 2 children + "border": "normal", + "floating": "auto_off", + "layout": "splith", + "percent": null, + "type": "con", + "nodes": [ + { + "border": "normal", + "current_border_width": 2, + "floating": "auto_off", + "geometry": { + "height": 860, + "width": 1396, + "x": 1922, + "y": 38 + }, + "name": "Chromium1", + "percent": 0.5, + "swallows": [ + { + "class": "^Chromium$", + // "instance": "^chromium$", + // "title": "^Git\\ Tutorial\\ \\-\\ corp\\ \\-\\ Chromium$", + // "transient_for": "^$", + // "window_role": "^browser$" + } + ], + "type": "con" + }, + { + "border": "normal", + "current_border_width": 2, + "floating": "auto_off", + "geometry": { + "height": 1040, + "width": 956, + "x": 2, + "y": 38 + }, + "name": "Chromium2", + "percent": 0.5, + "swallows": [ + { + "class": "^Chromium$", + // "instance": "^chromium$", + // "title": "^Nutanix\\ \\-\\ Prod\\ \\-\\ Sign\\ In\\ \\-\\ Chromium$", + // "transient_for": "^$", + // "window_role": "^browser$" + } + ], + "type": "con" + } + ] +} + +EOT + $fh->flush; + $reply = cmd "append_layout $filename"; + ok(!$reply->[0]->{success}, 'IPC reply indicated success'); + + does_i3_live; + + # Move to a different workspace rendered the half-attached con’s con->parent + # invalid. + fresh_workspace; + + cmd '[urgent=latest] focus'; + $reply = cmd 'scratchpad show'; + + does_i3_live; + + close($fh); +}; + ################################################################################ # wrong percent key in a child node ################################################################################ From 72c972a36c187584106c8792b8930d2aba468448 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 13 Sep 2017 16:39:44 +0200 Subject: [PATCH 048/187] AnyEvent-I3: use Carp for easier debugging (includes stacktraces) --- AnyEvent-I3/lib/AnyEvent/I3.pm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm index 75845ccd..8598f850 100644 --- a/AnyEvent-I3/lib/AnyEvent/I3.pm +++ b/AnyEvent-I3/lib/AnyEvent/I3.pm @@ -9,6 +9,7 @@ use AnyEvent::Socket; use AnyEvent; use Encode; use Scalar::Util qw(tainted); +use Carp; =head1 NAME @@ -186,7 +187,7 @@ sub new { # We use getpwuid() instead of $ENV{HOME} because the latter is tainted # and thus produces warnings when running tests with perl -T my $home = (getpwuid($<))[7]; - die "Could not get home directory" unless $home and -d $home; + confess "Could not get home directory" unless $home and -d $home; $path =~ s/~/$home/g; } @@ -330,9 +331,9 @@ scalar), if specified. sub message { my ($self, $type, $content) = @_; - die "No message type specified" unless defined($type); + confess "No message type specified" unless defined($type); - die "No connection to i3" unless defined($self->{ipchdl}); + confess "No connection to i3" unless defined($self->{ipchdl}); my $payload = ""; if ($content) { @@ -373,7 +374,7 @@ sub _ensure_connection { return if defined($self->{ipchdl}); - $self->connect->recv or die "Unable to connect to i3 (socket path " . $self->{path} . ")"; + $self->connect->recv or confess "Unable to connect to i3 (socket path " . $self->{path} . ")"; } =head2 get_workspaces From c45c002bad141add5b72a7e37ae930cf14f30f18 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 13 Sep 2017 17:14:51 +0200 Subject: [PATCH 049/187] validate JSON before loading This commit also introduces slurp() which reads a file in its entirety. Using this function instead of doing IO in the functions in load_layout.c again and again makes the code cleaner (fixing at least two memory leaks) and avoids re-reading the same file 3 times. related to #2755 --- include/load_layout.h | 10 +++- include/util.h | 8 +++ src/commands.c | 24 +++++++-- src/load_layout.c | 120 +++++++++++++++++------------------------- src/tree.c | 20 +++++-- src/util.c | 32 +++++++++++ 6 files changed, 131 insertions(+), 83 deletions(-) diff --git a/include/load_layout.h b/include/load_layout.h index 0dd81318..9205800f 100644 --- a/include/load_layout.h +++ b/include/load_layout.h @@ -31,6 +31,12 @@ typedef enum { * determine whether the file contains workspaces or regular containers, which * is important to know when deciding where (and how) to append the contents. * */ -json_content_t json_determine_content(const char *filename); +json_content_t json_determine_content(const char *buf, const size_t len); -void tree_append_json(Con *con, const char *filename, char **errormsg); +/** + * Returns true if the provided JSON could be parsed by yajl. + * + */ +bool json_validate(const char *buf, const size_t len); + +void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg); diff --git a/include/util.h b/include/util.h index 6c5fc4c6..de6fa568 100644 --- a/include/util.h +++ b/include/util.h @@ -164,3 +164,11 @@ void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it); * if the number could be parsed. */ bool parse_long(const char *str, long *out, int base); + +/** + * Slurp reads path in its entirety into buf, returning the length of the file + * or -1 if the file could not be read. buf is set to a buffer of appropriate + * size, or NULL if -1 is returned. + * + */ +ssize_t slurp(const char *path, char **buf); diff --git a/src/commands.c b/src/commands.c index faee3916..2697d6e1 100644 --- a/src/commands.c +++ b/src/commands.c @@ -773,13 +773,25 @@ void cmd_append_layout(I3_CMD, const char *cpath) { /* Make sure we allow paths like '~/.i3/layout.json' */ path = resolve_tilde(path); - json_content_t content = json_determine_content(path); + char *buf = NULL; + ssize_t len; + if ((len = slurp(path, &buf)) < 0) { + /* slurp already logged an error. */ + goto out; + } + + if (!json_validate(buf, len)) { + ELOG("Could not parse \"%s\" as JSON, not loading.\n", path); + yerror("Could not parse \"%s\" as JSON.", path); + goto out; + } + + json_content_t content = json_determine_content(buf, len); LOG("JSON content = %d\n", content); if (content == JSON_CONTENT_UNKNOWN) { ELOG("Could not determine the contents of \"%s\", not loading.\n", path); yerror("Could not determine the contents of \"%s\".", path); - free(path); - return; + goto out; } Con *parent = focused; @@ -795,7 +807,7 @@ void cmd_append_layout(I3_CMD, const char *cpath) { } DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused); char *errormsg = NULL; - tree_append_json(parent, path, &errormsg); + tree_append_json(parent, buf, len, &errormsg); if (errormsg != NULL) { yerror(errormsg); free(errormsg); @@ -820,8 +832,10 @@ void cmd_append_layout(I3_CMD, const char *cpath) { if (content == JSON_CONTENT_WORKSPACE) ipc_send_workspace_event("restored", parent, NULL); - free(path); cmd_output->needs_tree_render = true; +out: + free(path); + free(buf); } /* diff --git a/src/load_layout.c b/src/load_layout.c index 0fa3e85b..071b3ccd 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -69,9 +69,9 @@ static int json_start_map(void *ctx) { json_node->name = NULL; json_node->parent = parent; } - /* json_node is incomplete and should be removed if parsing fails */ - incomplete++; - DLOG("incomplete = %d\n", incomplete); + /* json_node is incomplete and should be removed if parsing fails */ + incomplete++; + DLOG("incomplete = %d\n", incomplete); } } return 1; @@ -170,8 +170,8 @@ static int json_end_map(void *ctx) { LOG("Creating window\n"); x_con_init(json_node); json_node = json_node->parent; - incomplete--; - DLOG("incomplete = %d\n", incomplete); + incomplete--; + DLOG("incomplete = %d\n", incomplete); } if (parsing_swallows && swallow_is_empty) { @@ -538,36 +538,42 @@ static int json_determine_content_string(void *ctx, const unsigned char *val, si return 0; } +/* + * Returns true if the provided JSON could be parsed by yajl. + * + */ +bool json_validate(const char *buf, const size_t len) { + bool valid = true; + yajl_handle hand = yajl_alloc(NULL, NULL, NULL); + /* Allowing comments allows for more user-friendly layout files. */ + yajl_config(hand, yajl_allow_comments, true); + /* Allow multiple values, i.e. multiple nodes to attach */ + yajl_config(hand, yajl_allow_multiple_values, true); + + setlocale(LC_NUMERIC, "C"); + if (yajl_parse(hand, (const unsigned char *)buf, len) != yajl_status_ok) { + unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len); + ELOG("JSON parsing error: %s\n", str); + yajl_free_error(hand, str); + valid = false; + } + setlocale(LC_NUMERIC, ""); + + yajl_complete_parse(hand); + yajl_free(hand); + + return valid; +} + /* Parses the given JSON file until it encounters the first “type” property to * determine whether the file contains workspaces or regular containers, which * is important to know when deciding where (and how) to append the contents. * */ -json_content_t json_determine_content(const char *filename) { - FILE *f; - if ((f = fopen(filename, "r")) == NULL) { - ELOG("Cannot open file \"%s\"\n", filename); - return JSON_CONTENT_UNKNOWN; - } - struct stat stbuf; - if (fstat(fileno(f), &stbuf) != 0) { - ELOG("Cannot fstat() \"%s\"\n", filename); - fclose(f); - return JSON_CONTENT_UNKNOWN; - } - char *buf = smalloc(stbuf.st_size); - int n = fread(buf, 1, stbuf.st_size, f); - if (n != stbuf.st_size) { - ELOG("File \"%s\" could not be read entirely, not loading.\n", filename); - fclose(f); - return JSON_CONTENT_UNKNOWN; - } - DLOG("read %d bytes\n", n); +json_content_t json_determine_content(const char *buf, const size_t len) { // We default to JSON_CONTENT_CON because it is legal to not include // “"type": "con"” in the JSON files for better readability. content_result = JSON_CONTENT_CON; content_level = 0; - yajl_gen g; - yajl_handle hand; static yajl_callbacks callbacks = { .yajl_string = json_determine_content_string, .yajl_map_key = json_key, @@ -576,51 +582,27 @@ json_content_t json_determine_content(const char *filename) { .yajl_end_map = json_determine_content_shallower, .yajl_end_array = json_determine_content_shallower, }; - g = yajl_gen_alloc(NULL); - hand = yajl_alloc(&callbacks, NULL, (void *)g); + yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL); /* Allowing comments allows for more user-friendly layout files. */ yajl_config(hand, yajl_allow_comments, true); /* Allow multiple values, i.e. multiple nodes to attach */ yajl_config(hand, yajl_allow_multiple_values, true); - yajl_status stat; setlocale(LC_NUMERIC, "C"); - stat = yajl_parse(hand, (const unsigned char *)buf, n); + const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len); if (stat != yajl_status_ok && stat != yajl_status_client_canceled) { - unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n); + unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len); ELOG("JSON parsing error: %s\n", str); yajl_free_error(hand, str); } setlocale(LC_NUMERIC, ""); yajl_complete_parse(hand); - - fclose(f); + yajl_free(hand); return content_result; } -void tree_append_json(Con *con, const char *filename, char **errormsg) { - FILE *f; - if ((f = fopen(filename, "r")) == NULL) { - ELOG("Cannot open file \"%s\"\n", filename); - return; - } - struct stat stbuf; - if (fstat(fileno(f), &stbuf) != 0) { - ELOG("Cannot fstat() \"%s\"\n", filename); - fclose(f); - return; - } - char *buf = smalloc(stbuf.st_size); - int n = fread(buf, 1, stbuf.st_size, f); - if (n != stbuf.st_size) { - ELOG("File \"%s\" could not be read entirely, not loading.\n", filename); - fclose(f); - return; - } - DLOG("read %d bytes\n", n); - yajl_gen g; - yajl_handle hand; +void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg) { static yajl_callbacks callbacks = { .yajl_boolean = json_bool, .yajl_integer = json_int, @@ -631,13 +613,11 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { .yajl_end_map = json_end_map, .yajl_end_array = json_end_array, }; - g = yajl_gen_alloc(NULL); - hand = yajl_alloc(&callbacks, NULL, (void *)g); + yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL); /* Allowing comments allows for more user-friendly layout files. */ yajl_config(hand, yajl_allow_comments, true); /* Allow multiple values, i.e. multiple nodes to attach */ yajl_config(hand, yajl_allow_multiple_values, true); - yajl_status stat; json_node = con; to_focus = NULL; incomplete = 0; @@ -649,19 +629,19 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { parsing_focus = false; parsing_marks = false; setlocale(LC_NUMERIC, "C"); - stat = yajl_parse(hand, (const unsigned char *)buf, n); + const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len); if (stat != yajl_status_ok) { - unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, n); + unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len); ELOG("JSON parsing error: %s\n", str); if (errormsg != NULL) *errormsg = sstrdup((const char *)str); yajl_free_error(hand, str); - while (incomplete-- > 0) { - Con *parent = json_node->parent; - DLOG("freeing incomplete container %p\n", json_node); - con_free(json_node); - json_node = parent; - } + while (incomplete-- > 0) { + Con *parent = json_node->parent; + DLOG("freeing incomplete container %p\n", json_node); + con_free(json_node); + json_node = parent; + } } /* In case not all containers were restored, we need to fix the @@ -672,10 +652,8 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { setlocale(LC_NUMERIC, ""); yajl_complete_parse(hand); yajl_free(hand); - yajl_gen_free(g); - fclose(f); - free(buf); - if (to_focus) + if (to_focus) { con_focus(to_focus); + } } diff --git a/src/tree.c b/src/tree.c index 296a6a37..b3d2ce93 100644 --- a/src/tree.c +++ b/src/tree.c @@ -64,12 +64,19 @@ static Con *_create___i3(void) { * */ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { + bool result = false; char *globbed = resolve_tilde(path); if (!path_exists(globbed)) { LOG("%s does not exist, not restoring tree\n", globbed); - free(globbed); - return false; + goto out; + } + + char *buf = NULL; + ssize_t len; + if ((len = slurp(globbed, &buf)) < 0) { + /* slurp already logged an error. */ + goto out; } /* TODO: refactor the following */ @@ -81,8 +88,7 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { geometry->height}; focused = croot; - tree_append_json(focused, globbed, NULL); - free(globbed); + tree_append_json(focused, buf, len, NULL); DLOG("appended tree, using new root\n"); croot = TAILQ_FIRST(&(croot->nodes_head)); @@ -104,8 +110,12 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { } restore_open_placeholder_windows(croot); + result = true; - return true; +out: + free(globbed); + free(buf); + return result; } /* diff --git a/src/util.c b/src/util.c index 32c3c57e..cd5ee03e 100644 --- a/src/util.c +++ b/src/util.c @@ -475,3 +475,35 @@ bool parse_long(const char *str, long *out, int base) { *out = result; return true; } + +/* + * Slurp reads path in its entirety into buf, returning the length of the file + * or -1 if the file could not be read. buf is set to a buffer of appropriate + * size, or NULL if -1 is returned. + * + */ +ssize_t slurp(const char *path, char **buf) { + FILE *f; + if ((f = fopen(path, "r")) == NULL) { + ELOG("Cannot open file \"%s\": %s\n", path, strerror(errno)); + return -1; + } + struct stat stbuf; + if (fstat(fileno(f), &stbuf) != 0) { + ELOG("Cannot fstat() \"%s\": %s\n", path, strerror(errno)); + fclose(f); + return -1; + } + /* Allocate one extra NUL byte to make the buffer usable with C string + * functions. yajl doesn’t need this, but this makes slurp safer. */ + *buf = scalloc(stbuf.st_size + 1, 1); + size_t n = fread(*buf, 1, stbuf.st_size, f); + fclose(f); + if ((ssize_t)n != stbuf.st_size) { + ELOG("File \"%s\" could not be read entirely: got %zd, want %zd\n", path, n, stbuf.st_size); + free(buf); + *buf = NULL; + return -1; + } + return (ssize_t)n; +} From 100fad741fe79a2a0ea8364d6b70f57f6d0c17a7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 13 Sep 2017 18:38:27 +0200 Subject: [PATCH 050/187] Add .editorconfig (see editorconfig.org) (#2943) This file uses the same settings which we currently have in our vim modeline, but can be picked up by many different editors without having to add and maintain editor-specific modelines in all of our source files. --- .editorconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c25745f8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{c,h}] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true From d0b8f93d4f941eab0d81d09c31741de04a67333f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 17:48:22 +0200 Subject: [PATCH 051/187] travis: install xrandr in Docker container for t/533-randr15.t (#2949) --- travis/travis-base.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis/travis-base.Dockerfile b/travis/travis-base.Dockerfile index 5704d8e4..7eafb9fb 100644 --- a/travis/travis-base.Dockerfile +++ b/travis/travis-base.Dockerfile @@ -19,7 +19,7 @@ RUN apt-get update && \ dpkg-dev devscripts git equivs \ clang clang-format-3.8 \ lintian \ - libmodule-install-perl libanyevent-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libjson-xs-perl && \ + libmodule-install-perl libanyevent-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libjson-xs-perl x11-xserver-utils && \ rm -rf /var/lib/apt/lists/* # Install i3 build dependencies. From a5aed10d09817569091229ad5475752a4e390516 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 17:48:37 +0200 Subject: [PATCH 052/187] tests: unflake t/257-keypress-group1-fallback.t (#2946) fixes #2944 --- testcases/t/257-keypress-group1-fallback.t | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testcases/t/257-keypress-group1-fallback.t b/testcases/t/257-keypress-group1-fallback.t index 212dfd15..843b8fe6 100644 --- a/testcases/t/257-keypress-group1-fallback.t +++ b/testcases/t/257-keypress-group1-fallback.t @@ -89,6 +89,9 @@ is(listen_for_binding( sync_with_i3; is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events'); +# Disable the grp:alt_shift_toggle option, as we use Alt+Shift in other testcases. +system(q|setxkbmap us -option|); + exit_gracefully($pid); } From 3a31a76b2aca30dbfff14ae0ecb9637f01937300 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 17:49:02 +0200 Subject: [PATCH 053/187] Reorder tests to not use the same number (#2947) Distinct numbers make re-running individual tests easier by helping with tab-completion. Completeness verified using: % for i in $(seq 0 600) do files=$(ls testcases/t/$(printf "%03d" $i)-*.t 2>&- | wc -l) [ "$files" != "0" ] && [ "$files" != "1" ] && echo "clash: $i" done --- ...or_window_tilingfloating.t => 271-for_window_tilingfloating.t} | 0 .../t/{173-regress-focus-assign.t => 272-regress-focus-assign.t} | 0 .../t/{174-regress-focus-toggle.t => 273-regress-focus-toggle.t} | 0 .../t/{213-move-branch-position.t => 274-move-branch-position.t} | 0 testcases/t/{231-ipc-window-close.t => 275-ipc-window-close.t} | 0 testcases/t/{231-ipc-window-move.t => 276-ipc-window-move.t} | 0 testcases/t/{232-ipc-window-urgent.t => 277-ipc-window-urgent.t} | 0 .../{234-layout-restore-output.t => 278-layout-restore-output.t} | 0 ...lt-floating-border.t => 279-regress-default-floating-border.t} | 0 ...35-wm-class-change-handler.t => 280-wm-class-change-handler.t} | 0 ...{238-regress-reload-bindsym.t => 281-regress-reload-bindsym.t} | 0 ...oating-disable-crash.t => 282-tabbed-floating-disable-crash.t} | 0 .../t/{243-net-wm-state-hidden.t => 283-net-wm-state-hidden.t} | 0 testcases/t/{251-ewmh-visible-name.t => 284-ewmh-visible-name.t} | 0 testcases/t/{251-sticky.t => 285-sticky.t} | 0 ...oot-window-mouse-binding.t => 286-root-window-mouse-binding.t} | 0 testcases/t/{263-edge-borders.t => 287-edge-borders.t} | 0 ...63-i3-floating-window-atom.t => 288-i3-floating-window-atom.t} | 0 .../t/{264-ipc-shutdown-event.t => 289-ipc-shutdown-event.t} | 0 testcases/t/{264-keypress-numlock.t => 290-keypress-numlock.t} | 0 testcases/t/{265-swap.t => 291-swap.t} | 0 .../t/{528-workspace-next-prev.t => 535-workspace-next-prev.t} | 0 testcases/t/{529-net-wm-desktop_mm.t => 536-net-wm-desktop_mm.t} | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename testcases/t/{165-for_window_tilingfloating.t => 271-for_window_tilingfloating.t} (100%) rename testcases/t/{173-regress-focus-assign.t => 272-regress-focus-assign.t} (100%) rename testcases/t/{174-regress-focus-toggle.t => 273-regress-focus-toggle.t} (100%) rename testcases/t/{213-move-branch-position.t => 274-move-branch-position.t} (100%) rename testcases/t/{231-ipc-window-close.t => 275-ipc-window-close.t} (100%) rename testcases/t/{231-ipc-window-move.t => 276-ipc-window-move.t} (100%) rename testcases/t/{232-ipc-window-urgent.t => 277-ipc-window-urgent.t} (100%) rename testcases/t/{234-layout-restore-output.t => 278-layout-restore-output.t} (100%) rename testcases/t/{234-regress-default-floating-border.t => 279-regress-default-floating-border.t} (100%) rename testcases/t/{235-wm-class-change-handler.t => 280-wm-class-change-handler.t} (100%) rename testcases/t/{238-regress-reload-bindsym.t => 281-regress-reload-bindsym.t} (100%) rename testcases/t/{240-tabbed-floating-disable-crash.t => 282-tabbed-floating-disable-crash.t} (100%) rename testcases/t/{243-net-wm-state-hidden.t => 283-net-wm-state-hidden.t} (100%) rename testcases/t/{251-ewmh-visible-name.t => 284-ewmh-visible-name.t} (100%) rename testcases/t/{251-sticky.t => 285-sticky.t} (100%) rename testcases/t/{262-root-window-mouse-binding.t => 286-root-window-mouse-binding.t} (100%) rename testcases/t/{263-edge-borders.t => 287-edge-borders.t} (100%) rename testcases/t/{263-i3-floating-window-atom.t => 288-i3-floating-window-atom.t} (100%) rename testcases/t/{264-ipc-shutdown-event.t => 289-ipc-shutdown-event.t} (100%) rename testcases/t/{264-keypress-numlock.t => 290-keypress-numlock.t} (100%) rename testcases/t/{265-swap.t => 291-swap.t} (100%) rename testcases/t/{528-workspace-next-prev.t => 535-workspace-next-prev.t} (100%) rename testcases/t/{529-net-wm-desktop_mm.t => 536-net-wm-desktop_mm.t} (100%) diff --git a/testcases/t/165-for_window_tilingfloating.t b/testcases/t/271-for_window_tilingfloating.t similarity index 100% rename from testcases/t/165-for_window_tilingfloating.t rename to testcases/t/271-for_window_tilingfloating.t diff --git a/testcases/t/173-regress-focus-assign.t b/testcases/t/272-regress-focus-assign.t similarity index 100% rename from testcases/t/173-regress-focus-assign.t rename to testcases/t/272-regress-focus-assign.t diff --git a/testcases/t/174-regress-focus-toggle.t b/testcases/t/273-regress-focus-toggle.t similarity index 100% rename from testcases/t/174-regress-focus-toggle.t rename to testcases/t/273-regress-focus-toggle.t diff --git a/testcases/t/213-move-branch-position.t b/testcases/t/274-move-branch-position.t similarity index 100% rename from testcases/t/213-move-branch-position.t rename to testcases/t/274-move-branch-position.t diff --git a/testcases/t/231-ipc-window-close.t b/testcases/t/275-ipc-window-close.t similarity index 100% rename from testcases/t/231-ipc-window-close.t rename to testcases/t/275-ipc-window-close.t diff --git a/testcases/t/231-ipc-window-move.t b/testcases/t/276-ipc-window-move.t similarity index 100% rename from testcases/t/231-ipc-window-move.t rename to testcases/t/276-ipc-window-move.t diff --git a/testcases/t/232-ipc-window-urgent.t b/testcases/t/277-ipc-window-urgent.t similarity index 100% rename from testcases/t/232-ipc-window-urgent.t rename to testcases/t/277-ipc-window-urgent.t diff --git a/testcases/t/234-layout-restore-output.t b/testcases/t/278-layout-restore-output.t similarity index 100% rename from testcases/t/234-layout-restore-output.t rename to testcases/t/278-layout-restore-output.t diff --git a/testcases/t/234-regress-default-floating-border.t b/testcases/t/279-regress-default-floating-border.t similarity index 100% rename from testcases/t/234-regress-default-floating-border.t rename to testcases/t/279-regress-default-floating-border.t diff --git a/testcases/t/235-wm-class-change-handler.t b/testcases/t/280-wm-class-change-handler.t similarity index 100% rename from testcases/t/235-wm-class-change-handler.t rename to testcases/t/280-wm-class-change-handler.t diff --git a/testcases/t/238-regress-reload-bindsym.t b/testcases/t/281-regress-reload-bindsym.t similarity index 100% rename from testcases/t/238-regress-reload-bindsym.t rename to testcases/t/281-regress-reload-bindsym.t diff --git a/testcases/t/240-tabbed-floating-disable-crash.t b/testcases/t/282-tabbed-floating-disable-crash.t similarity index 100% rename from testcases/t/240-tabbed-floating-disable-crash.t rename to testcases/t/282-tabbed-floating-disable-crash.t diff --git a/testcases/t/243-net-wm-state-hidden.t b/testcases/t/283-net-wm-state-hidden.t similarity index 100% rename from testcases/t/243-net-wm-state-hidden.t rename to testcases/t/283-net-wm-state-hidden.t diff --git a/testcases/t/251-ewmh-visible-name.t b/testcases/t/284-ewmh-visible-name.t similarity index 100% rename from testcases/t/251-ewmh-visible-name.t rename to testcases/t/284-ewmh-visible-name.t diff --git a/testcases/t/251-sticky.t b/testcases/t/285-sticky.t similarity index 100% rename from testcases/t/251-sticky.t rename to testcases/t/285-sticky.t diff --git a/testcases/t/262-root-window-mouse-binding.t b/testcases/t/286-root-window-mouse-binding.t similarity index 100% rename from testcases/t/262-root-window-mouse-binding.t rename to testcases/t/286-root-window-mouse-binding.t diff --git a/testcases/t/263-edge-borders.t b/testcases/t/287-edge-borders.t similarity index 100% rename from testcases/t/263-edge-borders.t rename to testcases/t/287-edge-borders.t diff --git a/testcases/t/263-i3-floating-window-atom.t b/testcases/t/288-i3-floating-window-atom.t similarity index 100% rename from testcases/t/263-i3-floating-window-atom.t rename to testcases/t/288-i3-floating-window-atom.t diff --git a/testcases/t/264-ipc-shutdown-event.t b/testcases/t/289-ipc-shutdown-event.t similarity index 100% rename from testcases/t/264-ipc-shutdown-event.t rename to testcases/t/289-ipc-shutdown-event.t diff --git a/testcases/t/264-keypress-numlock.t b/testcases/t/290-keypress-numlock.t similarity index 100% rename from testcases/t/264-keypress-numlock.t rename to testcases/t/290-keypress-numlock.t diff --git a/testcases/t/265-swap.t b/testcases/t/291-swap.t similarity index 100% rename from testcases/t/265-swap.t rename to testcases/t/291-swap.t diff --git a/testcases/t/528-workspace-next-prev.t b/testcases/t/535-workspace-next-prev.t similarity index 100% rename from testcases/t/528-workspace-next-prev.t rename to testcases/t/535-workspace-next-prev.t diff --git a/testcases/t/529-net-wm-desktop_mm.t b/testcases/t/536-net-wm-desktop_mm.t similarity index 100% rename from testcases/t/529-net-wm-desktop_mm.t rename to testcases/t/536-net-wm-desktop_mm.t From 53e7f187108e61724a080a0dfd0e5bfa37cf9290 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 11:44:49 +0200 Subject: [PATCH 054/187] =?UTF-8?q?i3test::XTEST:=20don=E2=80=99t=20?= =?UTF-8?q?=E2=80=9Cuse=20i3test=E2=80=9D=20to=20avoid=20clobbering=20stat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, the $i3_autostart variable was accidentally overridden. --- testcases/lib/i3test.pm.in | 9 +-------- testcases/lib/i3test/Util.pm | 23 +++++++++++++++++++++++ testcases/lib/i3test/XTEST.pm | 3 ++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index 5e3f8b2d..0f28a7a9 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -793,14 +793,7 @@ sub get_socket_path { if ($cache && defined($_cached_socket_path)) { return $_cached_socket_path; } - - my $atom = $x->atom(name => 'I3_SOCKET_PATH'); - my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256); - my $reply = $x->get_property_reply($cookie->{sequence}); - my $socketpath = $reply->{value}; - if ($socketpath eq "/tmp/nested-$ENV{DISPLAY}") { - $socketpath .= '-activation'; - } + my $socketpath = i3test::Util::get_socket_path($x); $_cached_socket_path = $socketpath; return $socketpath; } diff --git a/testcases/lib/i3test/Util.pm b/testcases/lib/i3test/Util.pm index 74913681..725ca4e1 100644 --- a/testcases/lib/i3test/Util.pm +++ b/testcases/lib/i3test/Util.pm @@ -5,9 +5,13 @@ use strict; use warnings; use v5.10; +use X11::XCB qw(GET_PROPERTY_TYPE_ANY); +use X11::XCB::Connection; + use Exporter qw(import); our @EXPORT = qw( slurp + get_socket_path ); =encoding utf-8 @@ -38,6 +42,25 @@ sub slurp { return $content; } +=head2 get_socket_path([X11::XCB::Connection]) + +Gets the socket path from the C atom stored on the X11 root +window. + +=cut +sub get_socket_path { + my ($x) = @_; + $x //= X11::XCB::Connection->new(); + my $atom = $x->atom(name => 'I3_SOCKET_PATH'); + my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256); + my $reply = $x->get_property_reply($cookie->{sequence}); + my $socketpath = $reply->{value}; + if ($socketpath eq "/tmp/nested-$ENV{DISPLAY}") { + $socketpath .= '-activation'; + } + return $socketpath; +} + =head1 AUTHOR Michael Stapelberg diff --git a/testcases/lib/i3test/XTEST.pm b/testcases/lib/i3test/XTEST.pm index 3937b70a..b7a9cdc5 100644 --- a/testcases/lib/i3test/XTEST.pm +++ b/testcases/lib/i3test/XTEST.pm @@ -5,7 +5,8 @@ use strict; use warnings; use v5.10; -use i3test i3_autostart => 0; +use Test::More; +use i3test::Util qw(get_socket_path); use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib); use AnyEvent::I3; use ExtUtils::PkgConfig; From 02786155dc7f5962242681f53383a54230f76965 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 12:30:42 +0200 Subject: [PATCH 055/187] tests: use i3_config arg instead of precisely one launch_with_config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way, tests are shorter, and i3test’s invocation of launch_with_config parallelizes work better, using dont_block => 1. --- testcases/lib/SocketActivation.pm | 5 ++ testcases/lib/i3test.pm.in | 5 +- testcases/t/156-fullscreen-focus.t | 18 ++----- testcases/t/186-regress-assign-focus-parent.t | 8 +-- testcases/t/199-ipc-mode-event.t | 8 +-- testcases/t/200-urgency-timer.t | 13 +---- testcases/t/207-shmlog.t | 14 ++---- testcases/t/208-regress-floating-criteria.t | 8 +-- testcases/t/211-regress-urgency-assign.t | 17 +++---- testcases/t/222-regress-dock-resize.t | 11 +---- testcases/t/230-floating-fullscreen-restart.t | 7 +-- .../t/233-regress-manage-focus-unmapped.t | 8 +-- testcases/t/237-regress-assign-focus.t | 8 +-- testcases/t/241-consistent-center.t | 8 +-- ...244-new-workspace-floating-enable-center.t | 8 +-- testcases/t/248-regress-urgency-clear.t | 18 +++---- .../t/254-move-to-output-with-criteria.t | 8 +-- testcases/t/257-keypress-group1-fallback.t | 21 +++----- testcases/t/258-keypress-release.t | 18 +++---- .../t/263-config-reload-reverts-bind-mode.t | 8 +-- testcases/t/264-dock-criteria.t | 7 +-- testcases/t/271-for_window_tilingfloating.t | 17 +++---- testcases/t/272-regress-focus-assign.t | 16 ++---- .../t/279-regress-default-floating-border.t | 8 +-- testcases/t/280-wm-class-change-handler.t | 10 +--- testcases/t/281-regress-reload-bindsym.t | 11 +---- .../t/282-tabbed-floating-disable-crash.t | 8 +-- testcases/t/286-root-window-mouse-binding.t | 9 +--- testcases/t/291-swap.t | 9 +--- testcases/t/500-multi-monitor.t | 6 +-- testcases/t/501-scratchpad.t | 9 +--- testcases/t/502-focus-output.t | 9 +--- testcases/t/503-workspace.t | 7 +-- testcases/t/504-move-workspace-to-output.t | 12 ++--- testcases/t/505-scratchpad-resolution.t | 9 +--- testcases/t/507-workspace-move-crash.t | 11 +---- testcases/t/509-workspace_layout.t | 9 +--- testcases/t/510-focus-across-outputs.t | 11 +---- .../t/511-scratchpad-configure-request.t | 12 +---- testcases/t/512-move-wraps.t | 12 +---- testcases/t/513-move-workspace.t | 14 +----- testcases/t/514-ipc-workspace-multi-monitor.t | 12 +---- testcases/t/515-create-workspace.t | 7 +-- testcases/t/516-move.t | 14 ++---- testcases/t/517-regress-move-direction-ipc.t | 8 +-- testcases/t/518-interpret-workspace-numbers.t | 7 +-- testcases/t/519-mouse-warping.t | 15 ++---- .../t/520-regress-focus-direction-floating.t | 8 +-- testcases/t/521-ewmh-desktop-viewport.t | 8 +-- testcases/t/522-rename-assigned-workspace.t | 7 +-- testcases/t/523-move-position-center.t | 8 +-- testcases/t/524-move.t | 8 +-- testcases/t/525-i3bar-mouse-bindings.t | 49 +++++++++++++------ testcases/t/526-reconfigure-dock.t | 19 +++---- .../t/528-workspace-next-prev-reversed.t | 17 +++---- testcases/t/529-net-wm-desktop.t | 24 +++------ testcases/t/530-bug-2229.t | 8 +-- testcases/t/531-fullscreen-on-given-output.t | 10 +--- testcases/t/534-dont-warp.t | 12 +---- testcases/t/535-workspace-next-prev.t | 17 +++---- testcases/t/536-net-wm-desktop_mm.t | 32 +++++------- 61 files changed, 187 insertions(+), 538 deletions(-) diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm index 5951fd26..5a5a4484 100644 --- a/testcases/lib/SocketActivation.pm +++ b/testcases/lib/SocketActivation.pm @@ -49,6 +49,11 @@ sub activate_i3 { die "could not fork()"; } if ($pid == 0) { + # Start a process group so that in the parent, we can kill the entire + # process group and immediately kill i3bar and any other child + # processes. + setpgrp; + $ENV{LISTEN_PID} = $$; $ENV{LISTEN_FDS} = 1; delete $ENV{DESKTOP_STARTUP_ID}; diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index 0f28a7a9..a9cfba37 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -126,7 +126,7 @@ END { exit_gracefully($i3_pid, "/tmp/nested-$ENV{DISPLAY}"); } else { - kill(9, $i3_pid) + kill(-9, $i3_pid) or $tester->BAIL_OUT("could not kill i3"); waitpid $i3_pid, 0; @@ -138,8 +138,9 @@ sub import { my $pkg = caller; $i3_autostart = delete($args{i3_autostart}) // 1; + my $i3_config = delete($args{i3_config}) // '-default'; - my $cv = launch_with_config('-default', dont_block => 1) + my $cv = launch_with_config($i3_config, dont_block => 1) if $i3_autostart; my $test_more_args = ''; diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index b337de9a..eefdac9f 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -18,22 +18,16 @@ # the time of launching the new one. Also make sure that focusing containers # in other workspaces work even when there is a fullscreen container. # -use i3test i3_autostart => 0; - -# Screen setup looks like this: -# +----+----+ -# | S1 | S2 | -# +----+----+ -my $config = < < 0; - -my $config = < < "testcase"); is_num_children('targetws', 3, 'new window opened next to last one'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/199-ipc-mode-event.t b/testcases/t/199-ipc-mode-event.t index 43f7b178..62675b44 100644 --- a/testcases/t/199-ipc-mode-event.t +++ b/testcases/t/199-ipc-mode-event.t @@ -15,9 +15,7 @@ # (unless you are already familiar with Perl) # # Verifies that the IPC 'mode' event is sent when modes are changed. -use i3test i3_autostart => 0; - -my $config = < <connect->recv; @@ -52,6 +48,4 @@ $t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0); }); ok($cv->recv, 'Mode event received'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/200-urgency-timer.t b/testcases/t/200-urgency-timer.t index b6ebc876..bd0b4078 100644 --- a/testcases/t/200-urgency-timer.t +++ b/testcases/t/200-urgency-timer.t @@ -20,20 +20,13 @@ # use List::Util qw(first); -use i3test i3_autostart => 0; -use Time::HiRes qw(sleep); - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < < 0; +use i3test; use IPC::Run qw(run); use File::Temp; @@ -22,14 +22,8 @@ use File::Temp; # 1: test that shared memory logging does not work yet ################################################################################ -my $config = < 0; - -my $config = <<'EOT'; +use i3test i3_config => < 'Borderless window', wm_class => 'special', @@ -43,6 +39,4 @@ cmd '[class="^special$"] focus'; does_i3_live; -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/211-regress-urgency-assign.t b/testcases/t/211-regress-urgency-assign.t index 57d8b434..1e1df7be 100644 --- a/testcases/t/211-regress-urgency-assign.t +++ b/testcases/t/211-regress-urgency-assign.t @@ -20,7 +20,12 @@ # # Ticket: #1086 # Bug still in: 4.6-62-g7098ef6 -use i3test i3_autostart => 0; +use i3test i3_config => < 0, my @urgent = grep { $_->{urgent} } @{get_ws_content('nonvisible')}; isnt(@urgent, 0, 'urgent window(s) found on destination workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/222-regress-dock-resize.t b/testcases/t/222-regress-dock-resize.t index b9f9a797..bfd7aa4b 100644 --- a/testcases/t/222-regress-dock-resize.t +++ b/testcases/t/222-regress-dock-resize.t @@ -18,18 +18,11 @@ # client. # Ticket: #1201 # Bug still in: 4.7.2-107-g9b03be6 -use i3test i3_autostart => 0; - -my $config = <<'EOT'; +use i3test i3_config => <connect()->recv; - my $window = open_window( wm_class => 'special', window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'), @@ -39,6 +32,4 @@ cmd('[class="special"] resize grow height 160 px or 16 ppt'); does_i3_live; -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/230-floating-fullscreen-restart.t b/testcases/t/230-floating-fullscreen-restart.t index e4d51e2e..9d1ac70c 100644 --- a/testcases/t/230-floating-fullscreen-restart.t +++ b/testcases/t/230-floating-fullscreen-restart.t @@ -18,16 +18,13 @@ # and that they keep their geometry. # Ticket: #1263 # Bug still in: 4.7.2-200-g570b572 -use i3test i3_autostart => 0; - -my $config = < < '__i3-test-window'); @@ -46,6 +43,4 @@ $floating_win = $nodes->{floating_nodes}->[0]->{nodes}->[0]; is($floating_win->{fullscreen_mode}, 1, 'floating window still in fullscreen mode'); is_deeply($floating_win->{geometry}, $old_geometry, 'floating window geometry still the same'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/233-regress-manage-focus-unmapped.t b/testcases/t/233-regress-manage-focus-unmapped.t index 1b78f253..3f47970c 100644 --- a/testcases/t/233-regress-manage-focus-unmapped.t +++ b/testcases/t/233-regress-manage-focus-unmapped.t @@ -19,9 +19,7 @@ # which can lead to complications # Ticket: #1283 # Bug still in: 4.8-24-g60070de -use i3test i3_autostart => 0; - -my $config = <<'EOT'; +use i3test i3_config => <<'EOT'; # i3 config file (v4) font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 @@ -29,8 +27,6 @@ for_window [class="^special_kill$"] kill for_window [class="^special_scratchpad$"] move scratchpad EOT -my $pid = launch_with_config($config); - my $win = open_window; my $scratch_window = open_window( @@ -53,6 +49,4 @@ sync_with_i3; is($x->input_focus, $win->{id}, 'an assignment that kills a window should not disturb focus'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/237-regress-assign-focus.t b/testcases/t/237-regress-assign-focus.t index e9cb537e..e193c021 100644 --- a/testcases/t/237-regress-assign-focus.t +++ b/testcases/t/237-regress-assign-focus.t @@ -18,9 +18,7 @@ # assigned to an invisible workspace) will not crash i3. # Ticket: #1338 # Bug still in: 4.8-91-g294d52e -use i3test i3_autostart => 0; - -my $config = < < 0; - -my $config = < <rect; is($new->{x}, $child->{x}, 'x coordinates match'); is($new->{y}, $child->{y}, 'y coordinates match'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/244-new-workspace-floating-enable-center.t b/testcases/t/244-new-workspace-floating-enable-center.t index dbc9a80a..b1c7fca0 100644 --- a/testcases/t/244-new-workspace-floating-enable-center.t +++ b/testcases/t/244-new-workspace-floating-enable-center.t @@ -17,9 +17,7 @@ # Ensures that 'move workspace $new, floating enable' on a marked window # leaves the window centered on the new workspace. # Bug still in: 4.10.2-137-ga4f0ed6 -use i3test i3_autostart => 0; - -my $config = < <{x} + $pos->{width} / 2), int($x->root->rect->width / 2), is(int($pos->{y} + $pos->{height} / 2), int($x->root->rect->height / 2), 'y coordinates match'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/248-regress-urgency-clear.t b/testcases/t/248-regress-urgency-clear.t index 10ef3774..314a8511 100644 --- a/testcases/t/248-regress-urgency-clear.t +++ b/testcases/t/248-regress-urgency-clear.t @@ -18,7 +18,12 @@ # to focus_on_window_activation=urgent), hence the application not clearing it. # Ticket: #1825 # Bug still in: 4.10.3-253-g03799dd -use i3test i3_autostart => 0; +use i3test i3_config => <send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); } -my $config = <<'EOT'; -# i3 config file (v4) -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 - -focus_on_window_activation urgent -EOT - -my $pid = launch_with_config($config); -my $i3 = i3(get_socket_path(0)); my $ws = fresh_workspace; my $first = open_window; my $second = open_window; @@ -64,6 +60,4 @@ cmd '[urgent=latest] focus'; sync_with_i3; is($x->input_focus, $second->id, 'second window still focused'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/254-move-to-output-with-criteria.t b/testcases/t/254-move-to-output-with-criteria.t index b27ded3e..7d8229dd 100644 --- a/testcases/t/254-move-to-output-with-criteria.t +++ b/testcases/t/254-move-to-output-with-criteria.t @@ -17,17 +17,13 @@ # Verifies that "move container to output" works correctly when # used with command criteria. # Bug still in: 4.10.4-349-gee5db87 -use i3test i3_autostart => 0; - -my $config = < < 0); my $ws_top_right = fresh_workspace(output => 1); my $ws_bottom_left = fresh_workspace(output => 2); @@ -45,6 +41,4 @@ is_num_children($ws_top_right, 1, 'one container on the upper right workspace'); is_num_children($ws_bottom_left, 0, 'no containers on the lower left workspace'); is_num_children($ws_bottom_right, 1, 'one container on the lower right workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/257-keypress-group1-fallback.t b/testcases/t/257-keypress-group1-fallback.t index 843b8fe6..1c19cb78 100644 --- a/testcases/t/257-keypress-group1-fallback.t +++ b/testcases/t/257-keypress-group1-fallback.t @@ -19,7 +19,14 @@ # Ticket: #2062 # Bug still in: 4.11-103-gc8d51b4 # Bug introduced with commit 0e5180cae9e9295678e3f053042b559e82cb8c98 -use i3test i3_autostart => 0; +use i3test + i3_config => </dev/null|) != 0; -my $config = < 0; -use i3test::XTEST; -use ExtUtils::PkgConfig; - -SKIP: { - skip "libxcb-xkb too old (need >= 1.11)", 1 unless - ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11'); - -my $config = < <= 1.11)", 1 unless + ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11'); start_binding_capture; @@ -90,8 +86,6 @@ is(listen_for_binding( sync_with_i3; is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events'); -exit_gracefully($pid); - } done_testing; diff --git a/testcases/t/263-config-reload-reverts-bind-mode.t b/testcases/t/263-config-reload-reverts-bind-mode.t index d72d3d94..549a862b 100644 --- a/testcases/t/263-config-reload-reverts-bind-mode.t +++ b/testcases/t/263-config-reload-reverts-bind-mode.t @@ -18,9 +18,7 @@ # binding mode. # Ticket: #2228 # Bug still in: 4.11-262-geb631ce -use i3test i3_autostart => 0; - -my $config = < <timer(after => 0.5, cb => sub { $cv->send(0); }); ok($cv->recv, 'Mode event received'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/264-dock-criteria.t b/testcases/t/264-dock-criteria.t index cbbdeb0f..d4b3b689 100644 --- a/testcases/t/264-dock-criteria.t +++ b/testcases/t/264-dock-criteria.t @@ -16,15 +16,11 @@ # # Verifies that command or config criteria does not match dock clients # Bug still in: 4.12-38-ge690e3d -use i3test i3_autostart => 0; - -my $config = < < 0; +use i3test i3_config => <{marks}, [ 'tiled' ], "mark set for 'tiling' criterion"); cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion"); -exit_gracefully($pid); - ############################################################## done_testing; diff --git a/testcases/t/272-regress-focus-assign.t b/testcases/t/272-regress-focus-assign.t index b010963b..6b0aad0c 100644 --- a/testcases/t/272-regress-focus-assign.t +++ b/testcases/t/272-regress-focus-assign.t @@ -17,7 +17,11 @@ # Regression: Checks if focus is stolen when a window is managed which is # assigned to an invisible workspace # -use i3test i3_autostart => 0; +use i3test i3_config => <{focused}, 'current workspace still focused'); -exit_gracefully($pid); - $window->destroy; done_testing; diff --git a/testcases/t/279-regress-default-floating-border.t b/testcases/t/279-regress-default-floating-border.t index d5994f58..5563ec32 100644 --- a/testcases/t/279-regress-default-floating-border.t +++ b/testcases/t/279-regress-default-floating-border.t @@ -18,9 +18,7 @@ # not applied when the default tiling border is set to a pixel value. # Ticket: #1305 # Bug still in: 4.8-62-g7381b50 -use i3test i3_autostart => 0; - -my $config = < <{floating_nodes}}; is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/280-wm-class-change-handler.t b/testcases/t/280-wm-class-change-handler.t index ce237b57..38b8351d 100644 --- a/testcases/t/280-wm-class-change-handler.t +++ b/testcases/t/280-wm-class-change-handler.t @@ -19,16 +19,12 @@ # in criteria selection # Ticket: #1052 # Bug still in: 4.8-73-g6bf7f8e -use i3test i3_autostart => 0; -use X11::XCB qw(PROP_MODE_REPLACE); - -my $config = < <{window_properties}->{class}, 'a', 'Non-null-terminated strings should be handled correctly'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/281-regress-reload-bindsym.t b/testcases/t/281-regress-reload-bindsym.t index 6d5d12c3..ad81244e 100644 --- a/testcases/t/281-regress-reload-bindsym.t +++ b/testcases/t/281-regress-reload-bindsym.t @@ -16,9 +16,7 @@ # # Test that the binding event works properly # Ticket: #1210 -use i3test i3_autostart => 0; - -my $config = < <connect->recv; - qx(xdotool key r); does_i3_live; - exit_gracefully($pid); - } done_testing; diff --git a/testcases/t/282-tabbed-floating-disable-crash.t b/testcases/t/282-tabbed-floating-disable-crash.t index 7947158c..b4f1a4c2 100644 --- a/testcases/t/282-tabbed-floating-disable-crash.t +++ b/testcases/t/282-tabbed-floating-disable-crash.t @@ -18,16 +18,12 @@ # unfocused window within a tabbed container. # Ticket: #1484 # Bug still in: 4.9.1-124-g856e1f9 -use i3test i3_autostart => 0; - -my $config = < < 0; -use i3test::XTEST; - -my $config = < < 0; - -my $config = < < 0; - -my $config = < <{name} } @{$tree->{nodes}}; is_deeply(\@outputs, [ '__i3', 'fake-0', 'fake-1' ], 'multi-monitor outputs ok'); -exit_gracefully($pid); done_testing; diff --git a/testcases/t/501-scratchpad.t b/testcases/t/501-scratchpad.t index 0f9b0df0..5aad2504 100644 --- a/testcases/t/501-scratchpad.t +++ b/testcases/t/501-scratchpad.t @@ -18,17 +18,12 @@ # ticket #596, bug present until up to commit # 89dded044b4fffe78f9d70778748fabb7ac533e9. # -use i3test i3_autostart => 0; - -my $config = < < 0); verify_scratchpad_switch($first, $second); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/502-focus-output.t b/testcases/t/502-focus-output.t index cf297f0e..4b6fac40 100644 --- a/testcases/t/502-focus-output.t +++ b/testcases/t/502-focus-output.t @@ -16,16 +16,13 @@ # # Verifies the 'focus output' command works properly. -use i3test i3_autostart => 0; -use List::Util qw(first); - -my $config = < < 0; - -my $config = < < to [output] ' command works # use List::Util qw(first); -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < < 0; - -my $config = < <root->warp_pointer(0, 0); @@ -84,6 +79,4 @@ sync_with_i3; my $third = fresh_workspace(output => 1); verify_scratchpad_doesnt_move($third); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/507-workspace-move-crash.t b/testcases/t/507-workspace-move-crash.t index 9e80553b..ed5a9005 100644 --- a/testcases/t/507-workspace-move-crash.t +++ b/testcases/t/507-workspace-move-crash.t @@ -19,19 +19,12 @@ # Bug still in: 4.3-78-g66b389c # use List::Util qw(first); -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < < 0; - -my $config = < <{workspace_layout}, 'tabbed', 'workspace layout is "tabbed"'); - -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/510-focus-across-outputs.t b/testcases/t/510-focus-across-outputs.t index afa0ddef..8097f73f 100644 --- a/testcases/t/510-focus-across-outputs.t +++ b/testcases/t/510-focus-across-outputs.t @@ -17,19 +17,12 @@ # Tests that switching workspaces via 'focus $dir' never leaves a floating # window focused. # -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <input_focus, $second->id, 'second window focused'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/511-scratchpad-configure-request.t b/testcases/t/511-scratchpad-configure-request.t index 561d2435..851afb4a 100644 --- a/testcases/t/511-scratchpad-configure-request.t +++ b/testcases/t/511-scratchpad-configure-request.t @@ -17,21 +17,13 @@ # Tests that ConfigureRequests don’t make windows fall out of the scratchpad. # Ticket: #898 # Bug still in: 4.4-15-g770ead6 -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < < 0); my $right_ws = fresh_workspace(output => 1); @@ -49,6 +41,4 @@ is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad af $ws = get_ws($right_ws); is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad after ConfigureRequest'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/512-move-wraps.t b/testcases/t/512-move-wraps.t index 82d5a7cc..2c5a27af 100644 --- a/testcases/t/512-move-wraps.t +++ b/testcases/t/512-move-wraps.t @@ -18,21 +18,13 @@ # E.g. when you have a container on the right output and you move it to the # right, it should appear on the left output. # Bug still in: 4.4-106-g3cd4b8c -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < < 1); my $left = fresh_workspace(output => 0); @@ -51,6 +43,4 @@ cmd 'move container to output right'; is_num_children($left, 1, 'one container on left workspace'); is_num_children($right, 0, 'no containers on right workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/513-move-workspace.t b/testcases/t/513-move-workspace.t index 3e27a6c0..fb9f0e25 100644 --- a/testcases/t/513-move-workspace.t +++ b/testcases/t/513-move-workspace.t @@ -15,21 +15,13 @@ # (unless you are already familiar with Perl) # # Tests whether moving workspaces between outputs works correctly. -use i3test i3_autostart => 0; -use List::Util qw(first); - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <[1]->{window}, $win1->id, 'window 1 on workspace 5 after moving'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/514-ipc-workspace-multi-monitor.t b/testcases/t/514-ipc-workspace-multi-monitor.t index 61622ab0..f34ee233 100644 --- a/testcases/t/514-ipc-workspace-multi-monitor.t +++ b/testcases/t/514-ipc-workspace-multi-monitor.t @@ -17,20 +17,12 @@ # Ticket: #990 # Bug still in: 4.5.1-23-g82b5978 -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <connect()->recv; @@ -73,6 +65,4 @@ ok($event, 'Workspace "focus" event received'); is($event->{current}->{id}, $current_ws->{id}, 'Event gave correct current workspace'); is($event->{old}->{id}, $old_ws->{id}, 'Event gave correct old workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/515-create-workspace.t b/testcases/t/515-create-workspace.t index be790bf0..aad0693f 100644 --- a/testcases/t/515-create-workspace.t +++ b/testcases/t/515-create-workspace.t @@ -17,9 +17,7 @@ # Tests that new workspace names are taken from the config, # then from the first free number starting with 1. # -use i3test i3_autostart => 0; - -my $config = < <get_workspaces->recv; @@ -35,6 +32,4 @@ my $ws = $i3->get_workspaces->recv; is($ws->[0]->{name}, '1: eggs', 'new workspace uses config name'); is($ws->[1]->{name}, '2', 'naming continues with next free number'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/516-move.t b/testcases/t/516-move.t index 0d21ca31..3a4fa2ea 100644 --- a/testcases/t/516-move.t +++ b/testcases/t/516-move.t @@ -16,13 +16,7 @@ # # Tests if a simple 'move ' command will move containers across outputs. # -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <root->warp_pointer(0, 0); ##################################################################### # Try to move a single window across outputs in each direction @@ -101,6 +97,4 @@ is(scalar @{get_ws_content('left-top')}, 1, 'moved some window to left-bottom wo $compare_window = shift @{get_ws_content('left-top')}; is($social_window->name, $compare_window->{name}, 'moved correct window to left-bottom workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/517-regress-move-direction-ipc.t b/testcases/t/517-regress-move-direction-ipc.t index 383b2779..1a325ae7 100644 --- a/testcases/t/517-regress-move-direction-ipc.t +++ b/testcases/t/517-regress-move-direction-ipc.t @@ -18,9 +18,7 @@ # ipc event required for i3bar to be properly updated and redrawn. # # Bug still in: 4.6-195-g34232b8 -use i3test i3_autostart => 0; - -my $config = < <connect()->recv; @@ -78,6 +74,4 @@ ok($event, 'moving from workspace with one window triggered focus ipc event'); is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace'); is(@{$event->{current}->{nodes}}, 2, 'focus event gave the right number of windows on the workspace'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/518-interpret-workspace-numbers.t b/testcases/t/518-interpret-workspace-numbers.t index 577881f0..aa2e2a27 100644 --- a/testcases/t/518-interpret-workspace-numbers.t +++ b/testcases/t/518-interpret-workspace-numbers.t @@ -18,9 +18,7 @@ # assign any workspace of that number to the specified output. # Ticket: #1238 # Bug still in: 4.7.2-147-g3760a48 -use i3test i3_autostart => 0; - -my $config = < <connect->recv; @@ -72,6 +69,4 @@ is(get_output_for_workspace('1:override'), 'fake-0', 'Assignment rules should not be affected by the order assignments are declared') or diag 'Since workspace "1:override" is assigned by name to fake-0, it should open on fake-0'; -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/519-mouse-warping.t b/testcases/t/519-mouse-warping.t index 674f4cdb..5b7e2c17 100644 --- a/testcases/t/519-mouse-warping.t +++ b/testcases/t/519-mouse-warping.t @@ -11,21 +11,16 @@ # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf # (unless you are already familiar with Perl) -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <root->warp_pointer(0, 0); ###################################################### # Open one workspace with one window on both outputs # @@ -47,6 +42,4 @@ $x->root->warp_pointer(0, 0); # Ensure focus is still on workspace 2 is(focused_ws, '2', 'warped mouse cursor to (0, 0), focus still in workspace 2'); -# Exit gracefully -exit_gracefully($pid); done_testing; diff --git a/testcases/t/520-regress-focus-direction-floating.t b/testcases/t/520-regress-focus-direction-floating.t index ccef49e7..871f8045 100644 --- a/testcases/t/520-regress-focus-direction-floating.t +++ b/testcases/t/520-regress-focus-direction-floating.t @@ -17,9 +17,7 @@ # Ensure that `focus [direction]` will focus an existing floating con when no # tiling con exists on the output in [direction] when focusing across outputs # Bug still in: 4.7.2-204-g893dbae -use i3test i3_autostart => 0; - -my $config = < <input_focus, $win->id, 'Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction].'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/521-ewmh-desktop-viewport.t b/testcases/t/521-ewmh-desktop-viewport.t index 9e36090c..27354769 100644 --- a/testcases/t/521-ewmh-desktop-viewport.t +++ b/testcases/t/521-ewmh-desktop-viewport.t @@ -18,9 +18,7 @@ # properly on the root window. We interpret this as a list of x/y coordinate # pairs for the upper left corner of the respective outputs of the workspaces # Ticket: #1241 -use i3test i3_autostart => 0; - -my $config = < < 0; - -my $config = < <connect->recv; @@ -85,6 +82,4 @@ cmd 'rename workspace to baz'; is(get_output_for_workspace('baz'), 'fake-1', 'Renaming the workspace to a number and name should move it to the assigned output'); - -exit_gracefully($pid); done_testing; diff --git a/testcases/t/523-move-position-center.t b/testcases/t/523-move-position-center.t index 6b584245..f0a8a816 100644 --- a/testcases/t/523-move-position-center.t +++ b/testcases/t/523-move-position-center.t @@ -18,9 +18,7 @@ # the appropriate output. # Ticket: #1211 # Bug still in: 4.9.1-108-g037cb31 -use i3test i3_autostart => 0; - -my $config = < <{floating_nodes}}, 0, 'no floating nodes on left ws'); is(scalar @{get_ws('right')->{floating_nodes}}, 1, 'one floating node on right ws'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/524-move.t b/testcases/t/524-move.t index 473bf235..8cc20bc0 100644 --- a/testcases/t/524-move.t +++ b/testcases/t/524-move.t @@ -19,9 +19,7 @@ # Ticket: #1603 # Bug still in: 4.10.1-40-g0ad097e use List::Util qw(first); -use i3test i3_autostart => 0; - -my $config = < < 0; -use i3test::XTEST; - -my ($cv, $timer); -sub reset_test { - $cv = AE::cv; - $timer = AE::timer(1, 0, sub { $cv->send(0); }); -} - -my $config = < <send(0); }); +} -my $pid = launch_with_config($config); my $i3 = i3(get_socket_path()); $i3->connect()->recv; my $ws = fresh_workspace; @@ -63,8 +60,32 @@ $i3->subscribe({ }, })->recv; -my $con = $cv->recv; -ok($con, 'i3bar appeared'); +my $con; + +sub i3bar_present { + my ($nodes) = @_; + + for my $node (@{$nodes}) { + my $props = $node->{window_properties}; + if (defined($props) && $props->{class} eq 'i3bar') { + return 1; + } + } + + return 0 if !@{$nodes}; + + my @children = (map { @{$_->{nodes}} } @{$nodes}, + map { @{$_->{'floating_nodes'}} } @{$nodes}); + + return i3bar_present(\@children); +} + +if (i3bar_present($i3->get_tree->recv->{nodes})) { + ok(1, 'i3bar present'); +} else { + $con = $cv->recv; + ok($con, 'i3bar appeared'); +} my $left = open_window; my $right = open_window; @@ -108,6 +129,4 @@ $con = $cv->recv; is($con->{window}, $left->{id}, 'button 5 moves focus left'); reset_test; -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/526-reconfigure-dock.t b/testcases/t/526-reconfigure-dock.t index 9e3ed3f2..19887ac1 100644 --- a/testcases/t/526-reconfigure-dock.t +++ b/testcases/t/526-reconfigure-dock.t @@ -16,9 +16,14 @@ # # Test reconfiguration of dock clients. # Ticket: #1883 -use i3test i3_autostart => 0; +use i3test i3_config => < $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK') }); @@ -50,8 +47,6 @@ is(@docks, 1, 'there is still exactly one dock'); is($docks[0]->{rect}->{x}, 1024, 'dock client has moved to the other screen'); -exit_gracefully($pid); - ############################################################################### done_testing; diff --git a/testcases/t/528-workspace-next-prev-reversed.t b/testcases/t/528-workspace-next-prev-reversed.t index 00a9bbe4..b10addec 100644 --- a/testcases/t/528-workspace-next-prev-reversed.t +++ b/testcases/t/528-workspace-next-prev-reversed.t @@ -17,7 +17,12 @@ # Tests whether 'workspace next' works correctly. # use List::Util qw(first); -use i3test i3_autostart => 0; +use i3test i3_config => <root->warp_pointer(0, 0); sync_with_i3; @@ -124,6 +121,4 @@ assert_prev('2'); assert_prev('1'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/529-net-wm-desktop.t b/testcases/t/529-net-wm-desktop.t index 5f858051..63c0b6a6 100644 --- a/testcases/t/529-net-wm-desktop.t +++ b/testcases/t/529-net-wm-desktop.t @@ -16,7 +16,14 @@ # # Tests for _NET_WM_DESKTOP. # Ticket: #2153 -use i3test i3_autostart => 0; +use i3test i3_config => < 0; - -my $config = < <get_workspaces->recv; my @ws_names = map { $_->{name} } @$get_ws; +# TODO get rid of smartmatch ok(!('3' ~~ @ws_names), 'workspace 3 has been closed'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/531-fullscreen-on-given-output.t b/testcases/t/531-fullscreen-on-given-output.t index fd328653..b2040554 100644 --- a/testcases/t/531-fullscreen-on-given-output.t +++ b/testcases/t/531-fullscreen-on-given-output.t @@ -16,17 +16,13 @@ # # Tests that fullscreen windows appear on the output indicated by # their geometry -use i3test i3_autostart => 0; -use List::Util qw(first); - -my $config = < <{nodes}, $win_on_second_output->{id}); is($node1->{fullscreen_mode}, 1, "first window is fullscreen"); is($node2->{fullscreen_mode}, 1, "second window is fullscreen"); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/534-dont-warp.t b/testcases/t/534-dont-warp.t index 8f84f9ae..997340fa 100644 --- a/testcases/t/534-dont-warp.t +++ b/testcases/t/534-dont-warp.t @@ -18,13 +18,7 @@ # over an unfocused workspace. # Ticket: #2681 # Bug still in: 4.13-210-g80c23afa -use i3test i3_autostart => 0; - -# Ensure the pointer is at (0, 0) so that we really start on the first -# (the left) workspace. -$x->root->warp_pointer(0, 0); - -my $config = < <query_pointer_reply($cookie->{sequence}); cmp_ok($reply->{root_x}, '<', 1024, 'pointer still on fake-0'); cmp_ok($reply->{root_y}, '<', 768, 'pointer still on fake-0'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/535-workspace-next-prev.t b/testcases/t/535-workspace-next-prev.t index 1a83de23..ac80eab7 100644 --- a/testcases/t/535-workspace-next-prev.t +++ b/testcases/t/535-workspace-next-prev.t @@ -17,7 +17,12 @@ # Tests whether 'workspace next' works correctly. # use List::Util qw(first); -use i3test i3_autostart => 0; +use i3test i3_config => <root->warp_pointer(0, 0); sync_with_i3; @@ -124,6 +121,4 @@ assert_prev('2'); assert_prev('1'); -exit_gracefully($pid); - done_testing; diff --git a/testcases/t/536-net-wm-desktop_mm.t b/testcases/t/536-net-wm-desktop_mm.t index 77238946..6346ebb7 100644 --- a/testcases/t/536-net-wm-desktop_mm.t +++ b/testcases/t/536-net-wm-desktop_mm.t @@ -16,7 +16,19 @@ # # Tests for _NET_WM_DESKTOP. # Ticket: #2153 -use i3test i3_autostart => 0; +use i3test i3_config => <{value}); } -my $config = < Date: Thu, 14 Sep 2017 11:30:58 +0200 Subject: [PATCH 056/187] tests: remove the (broken) exit_gracefully check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I previously tried to fix the check, but could only come up with a fix which required removing our module pre-loading, which makes the tests considerably more expensive. Instead, let’s just remove the check. --- testcases/lib/TestWorker.pm | 9 +++------ testcases/lib/i3test.pm.in | 10 ++-------- testcases/t/000-load-deps.t | 1 - 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/testcases/lib/TestWorker.pm b/testcases/lib/TestWorker.pm index c56767c4..9716f7f1 100644 --- a/testcases/lib/TestWorker.pm +++ b/testcases/lib/TestWorker.pm @@ -47,12 +47,9 @@ sub worker { $worker->{ipc} = $ipc_child; + # Preload the i3test module: reduces user CPU from 25s to 18s require i3test; - # TODO: recycle $x - # unfortunately this fails currently with: - # Could not get reply for: xcb_intern_atom_reply at X11/XCB/Atom.pm line 22. - # $i3test::x = bless $x, 'i3test::X11'; worker_wait($worker, $outdir); exit 23; @@ -86,11 +83,11 @@ sub worker_wait { exit unless $file; - die "tried to launch nonexistend testfile $file: $!\n" + die "tried to launch nonexistent testfile $file: $!\n" unless -e $file; # start a new and self contained process: - # whatever happens in the testfile should *NOT* effect us. + # whatever happens in the testfile should *NOT* affect us. my $pid = fork // die "could not fork: $!"; if ($pid == 0) { diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in index a9cfba37..222babcf 100644 --- a/testcases/lib/i3test.pm.in +++ b/testcases/lib/i3test.pm.in @@ -100,14 +100,8 @@ my $i3_pid; my $i3_autostart; END { - - # testcases which start i3 manually should always call exit_gracefully - # on their own. Let’s see, whether they really did. - if (! $i3_autostart) { - return unless $i3_pid; - - $tester->ok(undef, 'testcase called exit_gracefully()'); - } + # Skip the remaining cleanup for testcases which set i3_autostart => 0: + return if !defined($i3_pid) && !$i3_autostart; # don't trigger SIGCHLD handler local $SIG{CHLD}; diff --git a/testcases/t/000-load-deps.t b/testcases/t/000-load-deps.t index e0408338..65795305 100644 --- a/testcases/t/000-load-deps.t +++ b/testcases/t/000-load-deps.t @@ -11,7 +11,6 @@ BEGIN { IPC::Run ExtUtils::PkgConfig Inline - Test::More ); for my $dep (@deps) { use_ok($dep) or BAIL_OUT(qq|The Perl module "$dep" could not be loaded. Please see http://build.i3wm.org/docs/testsuite.html#_installing_the_dependencies|); From 24462f98aed51e936a4c86e610bddc96ba35c9af Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 14 Sep 2017 13:35:26 +0200 Subject: [PATCH 057/187] tests: unflake tests by not starting i3bar --- testcases/t/264-dock-criteria.t | 5 +++++ testcases/t/504-move-workspace-to-output.t | 5 +++++ testcases/t/526-reconfigure-dock.t | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/testcases/t/264-dock-criteria.t b/testcases/t/264-dock-criteria.t index d4b3b689..1eddba71 100644 --- a/testcases/t/264-dock-criteria.t +++ b/testcases/t/264-dock-criteria.t @@ -19,6 +19,11 @@ use i3test i3_config => < < < Date: Thu, 14 Sep 2017 23:10:16 +0300 Subject: [PATCH 058/187] Add -name argument for Xephyr (#2937) This way you can assign the test windows to an empty workspace to avoid interacting with them (when xvfb-run is not an option): assign [instance="i3test"] workspace testing --- testcases/lib/StartXServer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/lib/StartXServer.pm b/testcases/lib/StartXServer.pm index d3042c07..9d4e40c7 100644 --- a/testcases/lib/StartXServer.pm +++ b/testcases/lib/StartXServer.pm @@ -105,7 +105,7 @@ sub start_xserver { for (1 .. $parallel) { my $socket = fork_xserver($keep_xserver_output, $displaynum, 'Xephyr', ":$displaynum", '-screen', '1280x800', - '-nolisten', 'tcp'); + '-nolisten', 'tcp', '-name', "i3test"); push(@displays, ":$displaynum"); push(@sockets_waiting, $socket); $displaynum++; From bfa76016de6f473f86adab0805407b625950e6a1 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Thu, 14 Sep 2017 15:51:49 +0000 Subject: [PATCH 059/187] Fix typo in con_parent_with_orientation description --- include/con.h | 2 +- src/con.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/con.h b/include/con.h index 6a7e31bc..6cd1ef3e 100644 --- a/include/con.h +++ b/include/con.h @@ -102,7 +102,7 @@ Con *con_get_output(Con *con); Con *con_get_workspace(Con *con); /** - * Searches parenst of the given 'con' until it reaches one with the specified + * Searches parents of the given 'con' until it reaches one with the specified * 'orientation'. Aborts when it comes across a floating_con. * */ diff --git a/src/con.c b/src/con.c index 9797afa6..04aacd32 100644 --- a/src/con.c +++ b/src/con.c @@ -403,7 +403,7 @@ Con *con_get_workspace(Con *con) { } /* - * Searches parenst of the given 'con' until it reaches one with the specified + * Searches parents of the given 'con' until it reaches one with the specified * 'orientation'. Aborts when it comes across a floating_con. * */ From eaf7a49e28b7ff8e1820a1b23350647574a0ed5a Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Mon, 28 Aug 2017 05:14:03 +0300 Subject: [PATCH 060/187] Allow assign to workspace by number Makes "assign [] workspace number " work in the same manner as "move to workspace number " instead of assigning the window to a workspace named "number ". config.spec is modified to expect a 'number' string and an extra argument is used in cfg_assign. For workspaces that don't exist yet, workspace_get is used as a fallback. This also allows the user to assign to " " eg "2: work" and the full name will be used if workspace number 2 doesn't exist yet. Fixes #2590. --- docs/userguide | 8 ++- include/config_directives.h | 2 +- include/data.h | 3 +- parser-specs/config.spec | 8 ++- src/config_directives.c | 10 ++- src/manage.c | 21 ++++++- testcases/t/166-assign.t | 107 +++++++++++++++++++++++++------- testcases/t/201-config-parser.t | 11 ++-- 8 files changed, 135 insertions(+), 35 deletions(-) diff --git a/docs/userguide b/docs/userguide index 0d5de3b9..52165e2d 100644 --- a/docs/userguide +++ b/docs/userguide @@ -766,7 +766,7 @@ considered. *Syntax*: ------------------------------------------------------------ -assign [→] [workspace] +assign [→] [workspace] [number] ------------------------------------------------------------ *Examples*: @@ -783,6 +783,12 @@ assign [class="^URxvt$"] → 2 # Assignment to a named workspace assign [class="^URxvt$"] → work +# Assign to the workspace with number 2, regardless of name +assign [class="^URxvt$"] → number 2 + +# You can also specify a number + name. If the workspace with number 2 exists, assign will skip the text part. +assign [class="^URxvt$"] → number "2: work" + # Start urxvt -name irssi assign [class="^URxvt$" instance="^irssi$"] → 3 ---------------------- diff --git a/include/config_directives.h b/include/config_directives.h index f35666f3..b729e728 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -57,7 +57,7 @@ CFGFUN(force_display_urgency_hint, const long duration_ms); CFGFUN(focus_on_window_activation, const char *mode); CFGFUN(show_marks, const char *value); CFGFUN(hide_edge_borders, const char *borders); -CFGFUN(assign, const char *workspace); +CFGFUN(assign, const char *workspace, bool is_number); CFGFUN(no_focus); CFGFUN(ipc_socket, const char *path); CFGFUN(restart_state, const char *path); diff --git a/include/data.h b/include/data.h index 69a79ade..54333e04 100644 --- a/include/data.h +++ b/include/data.h @@ -546,7 +546,8 @@ struct Assignment { A_ANY = 0, A_COMMAND = (1 << 0), A_TO_WORKSPACE = (1 << 1), - A_NO_FOCUS = (1 << 2) + A_NO_FOCUS = (1 << 2), + A_TO_WORKSPACE_NUMBER = (1 << 3) } type; /** the criteria to check if a window matches */ diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 4aa320bf..665b046a 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -151,8 +151,14 @@ state ASSIGN_WORKSPACE: -> 'workspace' -> + 'number' + -> ASSIGN_WORKSPACE_NUMBER workspace = string - -> call cfg_assign($workspace) + -> call cfg_assign($workspace, 0) + +state ASSIGN_WORKSPACE_NUMBER: + number = string + -> call cfg_assign($number, 1) # no_focus state NO_FOCUS: diff --git a/src/config_directives.c b/src/config_directives.c index 7ca6e102..376397e8 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -377,15 +377,21 @@ CFGFUN(color, const char *colorclass, const char *border, const char *background #undef APPLY_COLORS } -CFGFUN(assign, const char *workspace) { +CFGFUN(assign, const char *workspace, bool is_number) { if (match_is_empty(current_match)) { ELOG("Match is empty, ignoring this assignment\n"); return; } + + if (is_number && ws_name_to_number(workspace) == -1) { + ELOG("Could not parse initial part of \"%s\" as a number.\n", workspace); + return; + } + DLOG("New assignment, using above criteria, to workspace \"%s\".\n", workspace); Assignment *assignment = scalloc(1, sizeof(Assignment)); match_copy(&(assignment->match), current_match); - assignment->type = A_TO_WORKSPACE; + assignment->type = is_number ? A_TO_WORKSPACE_NUMBER : A_TO_WORKSPACE; assignment->dest.workspace = sstrdup(workspace); TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } diff --git a/src/manage.c b/src/manage.c index 86a361c3..e3596548 100644 --- a/src/manage.c +++ b/src/manage.c @@ -259,9 +259,26 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki Con *wm_desktop_ws = NULL; /* If not, check if it is assigned to a specific workspace */ - if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) { + if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE)) || + (assignment = assignment_for(cwindow, A_TO_WORKSPACE_NUMBER))) { DLOG("Assignment matches (%p)\n", match); - Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL); + + Con *assigned_ws = NULL; + if (assignment->type == A_TO_WORKSPACE_NUMBER) { + Con *output = NULL; + long parsed_num = ws_name_to_number(assignment->dest.workspace); + + /* This will only work for workspaces that already exist. */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + GREP_FIRST(assigned_ws, output_get_content(output), child->num == parsed_num); + } + } + /* A_TO_WORKSPACE type assignment or fallback from A_TO_WORKSPACE_NUMBER + * when the target workspace number does not exist yet. */ + if (!assigned_ws) { + assigned_ws = workspace_get(assignment->dest.workspace, NULL); + } + nc = con_descend_tiling_focused(assigned_ws); DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name); if (nc->type == CT_WORKSPACE) diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index 355058dd..68548831 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -33,6 +33,33 @@ sub open_special { return $window; } +sub test_workspace_assignment { + my $target_ws = "@_"; + + # initialize the target workspace, then go to a fresh one + ok(!($target_ws ~~ @{get_workspace_names()}), "$target_ws does not exist yet"); + cmd "workspace $target_ws"; + cmp_ok(@{get_ws_content($target_ws)}, '==', 0, "no containers on $target_ws yet"); + cmd 'open'; + cmp_ok(@{get_ws_content($target_ws)}, '==', 1, "one container on $target_ws"); + my $tmp = fresh_workspace; + + ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); + ok($target_ws ~~ @{get_workspace_names()}, "$target_ws does not exist yet"); + + # We use sync_with_i3 instead of wait_for_map here because i3 will not actually + # map the window -- it will be assigned to a different workspace and will only + # be mapped once you switch to that workspace + my $window = open_special(dont_map => 1); + $window->map; + sync_with_i3; + + ok(@{get_ws_content($tmp)} == 0, 'still no containers'); + ok(@{get_ws_content($target_ws)} == 2, "two containers on $target_ws"); + + return $window +} + ##################################################################### # start a window and see that it does not get assigned with an empty config ##################################################################### @@ -87,33 +114,67 @@ $window->destroy; exit_gracefully($pid); ##################################################################### -# start a window and see that it gets assigned to a workspace which has content -# already, next to the existing node. +# start a window and see that it gets assigned to a formerly unused +# numbered workspace +##################################################################### + +my $config_numbered = <destroy; + +exit_gracefully($pid); + +##################################################################### +# start a window and see that it gets assigned to a numbered +# workspace which has content already, next to the existing node. +##################################################################### + +$pid = launch_with_config($config_numbered); + +$window = test_workspace_assignment("2"); +$window->destroy; + +exit_gracefully($pid); + +##################################################################### +# start a window and see that it gets assigned to a numbered workspace with +# a name which has content already, next to the existing node. +##################################################################### + +$pid = launch_with_config($config_numbered); + +cmd 'workspace 2'; # Make sure that we are not testing for "2" again. +$window = test_workspace_assignment("2: targetws"); +$window->destroy; + +exit_gracefully($pid); + +##################################################################### +# start a window and see that it gets assigned to a workspace which +# has content already, next to the existing node. ##################################################################### $pid = launch_with_config($config); -# initialize the target workspace, then go to a fresh one -ok(!("targetws" ~~ @{get_workspace_names()}), 'targetws does not exist yet'); -cmd 'workspace targetws'; -cmp_ok(@{get_ws_content('targetws')}, '==', 0, 'no containers on targetws yet'); -cmd 'open'; -cmp_ok(@{get_ws_content('targetws')}, '==', 1, 'one container on targetws'); -$tmp = fresh_workspace; - -ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); -ok("targetws" ~~ @{get_workspace_names()}, 'targetws does not exist yet'); - - -# We use sync_with_i3 instead of wait_for_map here because i3 will not actually -# map the window -- it will be assigned to a different workspace and will only -# be mapped once you switch to that workspace -$window = open_special(dont_map => 1); -$window->map; -sync_with_i3; - -ok(@{get_ws_content($tmp)} == 0, 'still no containers'); -ok(@{get_ws_content('targetws')} == 2, 'two containers on targetws'); +test_workspace_assignment("targetws"); exit_gracefully($pid); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index fb3130d5..e8080a73 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -116,6 +116,7 @@ is(parser_calls($config), $config = <<'EOT'; assign [class="^Chrome"] 4 +assign [class="^Chrome"] workspace number 3 assign [class="^Chrome"] named workspace assign [class="^Chrome"] "quoted named workspace" assign [class="^Chrome"] → "quoted named workspace" @@ -123,13 +124,15 @@ EOT $expected = <<'EOT'; cfg_criteria_add(class, ^Chrome) -cfg_assign(4) +cfg_assign(4, 0) cfg_criteria_add(class, ^Chrome) -cfg_assign(named workspace) +cfg_assign(3, 1) cfg_criteria_add(class, ^Chrome) -cfg_assign(quoted named workspace) +cfg_assign(named workspace, 0) cfg_criteria_add(class, ^Chrome) -cfg_assign(quoted named workspace) +cfg_assign(quoted named workspace, 0) +cfg_criteria_add(class, ^Chrome) +cfg_assign(quoted named workspace, 0) EOT is(parser_calls($config), From 00912add46242f05bb9a1ca032dc3da5ed3919dd Mon Sep 17 00:00:00 2001 From: Orestis Date: Fri, 15 Sep 2017 12:21:35 +0300 Subject: [PATCH 061/187] Use numeric comparison in sort for $displaynum (#2952) Eg in the case of sockets: - X0 - X99 - X100 X99 would be picked instead of X100. --- testcases/lib/StartXServer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/lib/StartXServer.pm b/testcases/lib/StartXServer.pm index 9d4e40c7..49976394 100644 --- a/testcases/lib/StartXServer.pm +++ b/testcases/lib/StartXServer.pm @@ -87,7 +87,7 @@ sub start_xserver { # First get the last used display number, then increment it by one. # Effectively falls back to 1 if no X server is running. - my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*'); + my ($displaynum) = reverse sort { $a <=> $b } map{ /(\d+)$/ } glob($x_socketpath . '*'); $displaynum++; say "Starting $parallel Xephyr instances, starting at :$displaynum..."; From 480f688b44f962fa57fd6d44dc2e204724416cdb Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Thu, 14 Sep 2017 15:41:48 +0000 Subject: [PATCH 062/187] Fix erratic behavior with single container child jumping outputs This fixes a regression introduced in commit 4e88c10564ca5366c2578908f62ec56625a26718: when attempting to move the single child of a container in the direction of another output, i3 would move the window to the output, despite the window not being at the edge of its output, instead of moving it to its parent container. The bug occurred because the check for moving containers across outputs with non-default workspace layouts (issue #1603) did not actually verify that the moved window lies at the edge of the workspace, despite what its comment said. Fixes issue #2466. --- src/move.c | 3 +- testcases/t/537-move-single-to-output.t | 63 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 testcases/t/537-move-single-to-output.t diff --git a/src/move.c b/src/move.c index 70c8c788..3ecc69e4 100644 --- a/src/move.c +++ b/src/move.c @@ -248,7 +248,8 @@ void tree_move(Con *con, int direction) { ? AFTER : BEFORE); insert_con_into(con, target, position); - } else if (con->parent->parent->type == CT_WORKSPACE && + } else if (!next && + con->parent->parent->type == CT_WORKSPACE && con->parent->layout != L_DEFAULT && con_num_children(con->parent) == 1) { /* Con is the lone child of a non-default layout container at the edge diff --git a/testcases/t/537-move-single-to-output.t b/testcases/t/537-move-single-to-output.t new file mode 100644 index 00000000..db5b4925 --- /dev/null +++ b/testcases/t/537-move-single-to-output.t @@ -0,0 +1,63 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests that windows inside containers with a single child do not jump +# over other containers and erratically move across outputs. +# Ticket: #2466 +# Bug still in: 4.14-63-g75d11820 +use i3test i3_config => < Date: Sat, 16 Sep 2017 13:43:28 +0300 Subject: [PATCH 063/187] Add SWAP util macro --- include/util.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/util.h b/include/util.h index de6fa568..526ab881 100644 --- a/include/util.h +++ b/include/util.h @@ -57,6 +57,13 @@ #define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__) +#define SWAP(first, second, type) \ + do { \ + type tmp_SWAP = first; \ + first = second; \ + second = tmp_SWAP; \ + } while (0) + int min(int a, int b); int max(int a, int b); bool rect_contains(Rect rect, uint32_t x, uint32_t y); From fa5d8a0209bdd7d0f47414dbc21f4bf4ea41821e Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Wed, 6 Sep 2017 04:10:22 +0300 Subject: [PATCH 064/187] Make swap work with fullscreen windows Swap works like normal with fullscreen windows but swaps the fullscreen mode of the 2 containers after it's done. Fixes #2811 --- src/con.c | 23 +++++-- testcases/t/291-swap.t | 151 +++++++++++++++++++++++++++++++++++------ 2 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/con.c b/src/con.c index 04aacd32..a663db31 100644 --- a/src/con.c +++ b/src/con.c @@ -2276,15 +2276,14 @@ bool con_swap(Con *first, Con *second) { Con *current_ws = con_get_workspace(old_focus); const bool focused_within_first = (first == old_focus || con_has_parent(old_focus, first)); const bool focused_within_second = (second == old_focus || con_has_parent(old_focus, second)); + fullscreen_mode_t first_fullscreen_mode = first->fullscreen_mode; + fullscreen_mode_t second_fullscreen_mode = second->fullscreen_mode; - if (!con_fullscreen_permits_focusing(first_ws)) { - DLOG("Cannot swap because target workspace \"%s\" is obscured.\n", first_ws->name); - return false; + if (first_fullscreen_mode != CF_NONE) { + con_disable_fullscreen(first); } - - if (!con_fullscreen_permits_focusing(second_ws)) { - DLOG("Cannot swap because target workspace \"%s\" is obscured.\n", second_ws->name); - return false; + if (second_fullscreen_mode != CF_NONE) { + con_disable_fullscreen(second); } double first_percent = first->percent; @@ -2385,7 +2384,17 @@ bool con_swap(Con *first, Con *second) { second->percent = first_percent; fake->percent = 0.0; + SWAP(first_fullscreen_mode, second_fullscreen_mode, fullscreen_mode_t); + swap_end: + /* The two windows exchange their original fullscreen status */ + if (first_fullscreen_mode != CF_NONE) { + con_enable_fullscreen(first, first_fullscreen_mode); + } + if (second_fullscreen_mode != CF_NONE) { + con_enable_fullscreen(second, second_fullscreen_mode); + } + /* We don't actually need this since percentages-wise we haven't changed * anything, but we'll better be safe than sorry and just make sure as we'd * otherwise crash i3. */ diff --git a/testcases/t/291-swap.t b/testcases/t/291-swap.t index 53932597..f9ac3b6a 100644 --- a/testcases/t/291-swap.t +++ b/testcases/t/291-swap.t @@ -25,10 +25,17 @@ for_window[class="mark_B"] mark B EOT my ($ws, $ws1, $ws2, $ws3); -my ($nodes, $expected_focus, $A, $B, $F); +my ($node, $nodes, $expected_focus, $A, $B, $F); my ($result); +my @fullscreen_permutations = ([], ["A"], ["B"], ["A", "B"]); my @urgent; +sub fullscreen_windows { + my $ws = shift if @_; + + scalar grep { $_->{fullscreen_mode} != 0 } @{get_ws_content($ws)} +} + ############################################################################### # Invalid con_id should not crash i3 # See issue #2895. @@ -162,26 +169,114 @@ kill_all_windows; # | Y | B | Focus Stacks: # +---+---+ H2: B, Y ############################################################################### +for my $fullscreen (@fullscreen_permutations){ + $ws1 = fresh_workspace; + $A = open_window(wm_class => 'mark_A'); + $expected_focus = get_focused($ws1); + open_window; + cmd 'focus left'; + + $ws2 = fresh_workspace; + open_window; + $B = open_window(wm_class => 'mark_B'); + + my $A_fullscreen = "A" ~~ @$fullscreen || 0; + my $B_fullscreen = "B" ~~ @$fullscreen || 0; + $A->fullscreen($A_fullscreen); + $B->fullscreen($B_fullscreen); + sync_with_i3; + + cmd '[con_mark=B] swap container with mark A'; + + $nodes = get_ws_content($ws1); + $node = $nodes->[0]; + is($node->{window}, $B->{id}, 'B is on ws1:left'); + is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1'); + is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode'); + + $nodes = get_ws_content($ws2); + $node = $nodes->[1]; + is($node->{window}, $A->{id}, 'A is on ws2:right'); + is(get_focused($ws2), $expected_focus, 'A is focused'); + is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2'); + is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode'); + + kill_all_windows; +} + +############################################################################### +# Swap a non-fullscreen window with a fullscreen one in different workspaces. +# Layout: O1[ W1[ H1 ] W2[ B ] ] +# +# +---+---+ Layout: H1[ A F ] +# | A | F | Focus Stacks: +# +---+---+ H1: F, A +# +# +---+---+ +# | B | +# +---+---+ +############################################################################### $ws1 = fresh_workspace; + $A = open_window(wm_class => 'mark_A'); +$F = open_window(); +$F->fullscreen(1); $expected_focus = get_focused($ws1); -open_window; -cmd 'focus left'; $ws2 = fresh_workspace; -open_window; $B = open_window(wm_class => 'mark_B'); +$B->fullscreen(1); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws1); is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left'); +is(fullscreen_windows($ws1), 1, 'F still fullscreen in ws1'); +is(get_focused($ws1), $expected_focus, 'F is still focused'); $nodes = get_ws_content($ws2); -is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right'); -is(get_focused($ws2), $expected_focus, 'A is focused'); +is($nodes->[0]->{window}, $A->{id}, 'A is on ws1'); -kill_all_windows; +############################################################################### +# Try a more exotic layout with fullscreen containers. +# A and F are fullscreened as a stack of two vertical containers before the +# swap is performed. +# A is swapped with fullscreened window B which is in another workspace. +# +# +---+---+ Layout: H1[ X V1[ A F ] ] +# | | A | Focus Stacks: +# | X +---+ H1: V1, X +# | | F | V1: F, A +# +---+---+ +############################################################################### +$ws1 = fresh_workspace; + +open_window; +$A = open_window(wm_class => 'mark_A'); +cmd "split v"; +open_window; +cmd "focus parent"; +cmd "fullscreen enable"; +$F = fullscreen_windows($ws1); +$expected_focus = get_focused($ws1); + +$ws2 = fresh_workspace; +$B = open_window(wm_class => 'mark_B'); +$B->fullscreen(1); + +cmd '[con_mark=B] swap container with mark A'; + +sync_with_i3; +does_i3_live; + +$nodes = get_ws_content($ws1); +is($nodes->[1]->{nodes}->[0]->{window}, $B->{id}, 'B is on top right in ws1'); +is(get_focused($ws1), $expected_focus, 'The container of the stacked windows remains focused in ws1'); +is(fullscreen_windows($ws1), $F, 'Same amount of fullscreen windows in ws1'); + +$nodes = get_ws_content($ws2); +is($nodes->[0]->{window}, $A->{id}, 'A is on ws2'); +is(fullscreen_windows($ws2), 1, 'A is in fullscreen mode'); ############################################################################### # Swap two non-focused containers within the same workspace. @@ -232,27 +327,41 @@ kill_all_windows; # | F | # +---+ ############################################################################### -$ws1 = fresh_workspace; -$A = open_window(wm_class => 'mark_A'); +for my $fullscreen (@fullscreen_permutations){ + $ws1 = fresh_workspace; + $A = open_window(wm_class => 'mark_A'); -$ws2 = fresh_workspace; -$B = open_window(wm_class => 'mark_B'); + $ws2 = fresh_workspace; + $B = open_window(wm_class => 'mark_B'); -$ws3 = fresh_workspace; -open_window; -$expected_focus = get_focused($ws3); + $ws3 = fresh_workspace; + open_window; + $expected_focus = get_focused($ws3); -cmd '[con_mark=B] swap container with mark A'; + my $A_fullscreen = "A" ~~ @$fullscreen || 0; + my $B_fullscreen = "B" ~~ @$fullscreen || 0; + $A->fullscreen($A_fullscreen); + $B->fullscreen($B_fullscreen); + sync_with_i3; -$nodes = get_ws_content($ws1); -is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace'); + cmd '[con_mark=B] swap container with mark A'; -$nodes = get_ws_content($ws2); -is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace'); + $nodes = get_ws_content($ws1); + $node = $nodes->[0]; + is($node->{window}, $B->{id}, 'B is on the first workspace'); + is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1'); + is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode'); -is(get_focused($ws3), $expected_focus, 'F is still focused'); + $nodes = get_ws_content($ws2); + $node = $nodes->[0]; + is($node->{window}, $A->{id}, 'A is on the second workspace'); + is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2'); + is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode'); -kill_all_windows; + is(get_focused($ws3), $expected_focus, 'F is still focused'); + + kill_all_windows; +} ############################################################################### # Swap two non-focused containers with one being on a different workspace. From 5062dd61bc991988a146f298f4b8ed4ea81290a8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 16 Sep 2017 17:28:44 +0200 Subject: [PATCH 065/187] ipc: tree reply: document focus, nodes and floating_nodes (#2955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These haven’t ever changed, but were only included in the example, not in the list, so people might not have realized that these are safe for use. --- docs/ipc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/ipc b/docs/ipc index 65723577..ab2997fd 100644 --- a/docs/ipc +++ b/docs/ipc @@ -338,6 +338,16 @@ urgent (bool):: Whether this container (window or workspace) has the urgency hint set. focused (bool):: Whether this container is currently focused. +focus (array of integer):: + List of child node IDs (see +nodes+, +floating_nodes+ and +id+) in focus + order. Traversing the tree by following the first entry in this array + will result in eventually reaching the one node with +focused+ set to + true. +nodes (array of node):: + The tiling (i.e. non-floating) child containers of this node. +floating_nodes (array of node):: + The floating child containers of this node. Only non-empty on nodes with + type +workspace+. Please note that in the following example, I have left out some keys/values which are not relevant for the type of the node. Otherwise, the example would From 205dd7609695f3808f2cff0167ec836a7d66ec17 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sat, 16 Sep 2017 23:53:31 +0300 Subject: [PATCH 066/187] Prevent freeing of uninitialized pointer > variable 'buf' is used uninitialized whenever 'if' condition is true Note: freeing a NULL pointer is fine. --- src/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index b3d2ce93..7f466583 100644 --- a/src/tree.c +++ b/src/tree.c @@ -66,13 +66,13 @@ static Con *_create___i3(void) { bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { bool result = false; char *globbed = resolve_tilde(path); + char *buf = NULL; if (!path_exists(globbed)) { LOG("%s does not exist, not restoring tree\n", globbed); goto out; } - char *buf = NULL; ssize_t len; if ((len = slurp(globbed, &buf)) < 0) { /* slurp already logged an error. */ From f7a7c7778ba987c30276d2c30bd99b5e2a495909 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 00:24:15 +0300 Subject: [PATCH 067/187] Fix wrong call to free To confirm, assign n to a constant value and try to use the append_layout command. Without the change i3 crashes. --- src/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index cd5ee03e..ba0969c7 100644 --- a/src/util.c +++ b/src/util.c @@ -501,7 +501,7 @@ ssize_t slurp(const char *path, char **buf) { fclose(f); if ((ssize_t)n != stbuf.st_size) { ELOG("File \"%s\" could not be read entirely: got %zd, want %zd\n", path, n, stbuf.st_size); - free(buf); + free(*buf); *buf = NULL; return -1; } From 91c8c00afc146130c513233fd098707b7f4a2d94 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 01:14:47 +0300 Subject: [PATCH 068/187] Fix use of err after it is freed --- src/randr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/randr.c b/src/randr.c index bc791696..88cad7c5 100644 --- a/src/randr.c +++ b/src/randr.c @@ -1048,8 +1048,8 @@ void randr_init(int *event_base, const bool disable_randr15) { xcb_randr_query_version_reply( conn, xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err); if (err != NULL) { - free(err); ELOG("Could not query RandR version: X11 error code %d\n", err->error_code); + free(err); fallback_to_root_output(); return; } From e2bacc7df8ba09a552ddd3555f3c730ed5205664 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sat, 16 Sep 2017 20:14:35 +0300 Subject: [PATCH 069/187] Add con_move_to_output_name function --- include/con.h | 11 ++++++++++- src/commands.c | 20 +------------------- src/con.c | 25 +++++++++++++++++++++++-- src/manage.c | 2 +- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/include/con.h b/include/con.h index 6cd1ef3e..69292411 100644 --- a/include/con.h +++ b/include/con.h @@ -307,7 +307,16 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, * visible workspace on the given output. * */ -void con_move_to_output(Con *con, Output *output); +void con_move_to_output(Con *con, Output *output, bool fix_coordinates); + +/** + * Moves the given container to the currently focused container on the + * visible workspace on the output specified by the given name. + * The current output for the container is used to resolve relative names + * such as left, right, up, down. + * + */ +bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates); /** * Moves the given container to the given mark. diff --git a/src/commands.c b/src/commands.c index 2697d6e1..d7cdf219 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1044,25 +1044,7 @@ void cmd_move_con_to_output(I3_CMD, const char *name) { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); - Output *current_output = get_output_for_con(current->con); - assert(current_output != NULL); - - Output *output = get_output_from_string(current_output, name); - if (output == NULL) { - ELOG("Could not find output \"%s\", skipping.\n", name); - had_error = true; - continue; - } - - Con *ws = NULL; - GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); - if (ws == NULL) { - ELOG("Could not find a visible workspace on output %p.\n", output); - had_error = true; - continue; - } - - con_move_to_workspace(current->con, ws, true, false, false); + had_error |= !con_move_to_output_name(current->con, name, true); } cmd_output->needs_tree_render = true; diff --git a/src/con.c b/src/con.c index a663db31..8254672f 100644 --- a/src/con.c +++ b/src/con.c @@ -1284,12 +1284,33 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool * visible workspace on the given output. * */ -void con_move_to_output(Con *con, Output *output) { +void con_move_to_output(Con *con, Output *output, bool fix_coordinates) { Con *ws = NULL; GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); assert(ws != NULL); DLOG("Moving con %p to output %s\n", con, output_primary_name(output)); - con_move_to_workspace(con, ws, false, false, false); + con_move_to_workspace(con, ws, fix_coordinates, false, false); +} + +/* + * Moves the given container to the currently focused container on the + * visible workspace on the output specified by the given name. + * The current output for the container is used to resolve relative names + * such as left, right, up, down. + * + */ +bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates) { + Output *current_output = get_output_for_con(con); + assert(current_output != NULL); + + Output *output = get_output_from_string(current_output, name); + if (output == NULL) { + ELOG("Could not find output \"%s\"\n", name); + return false; + } + + con_move_to_output(con, output, fix_coordinates); + return true; } /* diff --git a/src/manage.c b/src/manage.c index f155603d..8087d563 100644 --- a/src/manage.c +++ b/src/manage.c @@ -384,7 +384,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki * needed e.g. for LibreOffice Impress multi-monitor * presentations to work out of the box. */ if (output != NULL) - con_move_to_output(nc, output); + con_move_to_output(nc, output, false); con_toggle_fullscreen(nc, CF_OUTPUT); } fs = NULL; From 1c975a1b8ca69a199d1d994c3bcfe07fb368a6cf Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 14:45:37 +0300 Subject: [PATCH 070/187] 166-assign.t: fix typo --- testcases/t/166-assign.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index 68548831..8ca62d1e 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -242,7 +242,7 @@ $tmp = fresh_workspace; ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); my @docked = get_dock_clients; -is(@docked, 0, 'one dock client yet'); +is(@docked, 0, 'no dock client yet'); $window = open_special( window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'), From 716a5b366065717be20432cb80a21802282ec823 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 13:10:58 +0300 Subject: [PATCH 071/187] 166-assign.t: improve open_special call --- testcases/t/166-assign.t | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index 8ca62d1e..07bddfff 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -21,12 +21,12 @@ use i3test i3_autostart => 0; sub open_special { my %args = @_; $args{name} //= 'special window'; + $args{wm_class} //= 'special'; # We use dont_map because i3 will not map the window on the current # workspace. Thus, open_window would time out in wait_for_map (2 seconds). my $window = open_window( %args, - wm_class => 'special', dont_map => 1, ); $window->map; @@ -50,8 +50,7 @@ sub test_workspace_assignment { # We use sync_with_i3 instead of wait_for_map here because i3 will not actually # map the window -- it will be assigned to a different workspace and will only # be mapped once you switch to that workspace - my $window = open_special(dont_map => 1); - $window->map; + my $window = open_special; sync_with_i3; ok(@{get_ws_content($tmp)} == 0, 'still no containers'); From 45d1e51857ef69d7012ce71222be32a323b3101b Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 15:20:47 +0300 Subject: [PATCH 072/187] Fix userguide formatting error --- docs/userguide | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/userguide b/docs/userguide index cc9b5a01..38b16e5c 100644 --- a/docs/userguide +++ b/docs/userguide @@ -2238,7 +2238,6 @@ bindsym $mod+x move container to output VGA1 bindsym $mod+x move container to output primary -------------------------------------------------------- -------------------------------- Note that you might not have a primary output configured yet. To do so, run: ------------------------- xrandr --output --primary From a35854ddf4c39de84660471652c6a90c6a221535 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sat, 16 Sep 2017 20:54:44 +0300 Subject: [PATCH 073/187] Allow assign to output Implements the "assign" part of issue #2764. --- docs/userguide | 18 +++++- include/config_directives.h | 1 + include/data.h | 4 +- parser-specs/config.spec | 8 ++- src/config_directives.c | 14 +++++ src/manage.c | 4 ++ testcases/t/166-assign.t | 121 +++++++++++++++++++++++++++++++++++- 7 files changed, 166 insertions(+), 4 deletions(-) diff --git a/docs/userguide b/docs/userguide index 38b16e5c..a911b08c 100644 --- a/docs/userguide +++ b/docs/userguide @@ -760,6 +760,10 @@ title change. As i3 will get the title as soon as the application maps the window (mapping means actually displaying it on the screen), you’d need to have to match on 'Firefox' in this case. +You can also assign a window to show up on a specific output. You can use RandR +names such as +VGA1+ or names relative to the output with the currently focused +workspace such as +left+ and +down+. + Assignments are processed by i3 in the order in which they appear in the config file. The first one which matches the window wins and later assignments are not considered. @@ -767,6 +771,7 @@ considered. *Syntax*: ------------------------------------------------------------ assign [→] [workspace] [number] +assign [→] output left|right|up|down|primary| ------------------------------------------------------------ *Examples*: @@ -791,9 +796,20 @@ assign [class="^URxvt$"] → number "2: work" # Start urxvt -name irssi assign [class="^URxvt$" instance="^irssi$"] → 3 + +# Assign urxvt to the output right of the current one +assign [class="^URxvt$"] → output right + +# Assign urxvt to the primary output +assign [class="^URxvt$"] → output primary ---------------------- -Note that the arrow is not required, it just looks good :-). If you decide to +Note that you might not have a primary output configured yet. To do so, run: +------------------------- +xrandr --output --primary +------------------------- + +Also, the arrow is not required, it just looks good :-). If you decide to use it, it has to be a UTF-8 encoded arrow, not `->` or something like that. To get the class and instance, you can use +xprop+. After clicking on the diff --git a/include/config_directives.h b/include/config_directives.h index b729e728..5318bae8 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -57,6 +57,7 @@ CFGFUN(force_display_urgency_hint, const long duration_ms); CFGFUN(focus_on_window_activation, const char *mode); CFGFUN(show_marks, const char *value); CFGFUN(hide_edge_borders, const char *borders); +CFGFUN(assign_output, const char *output); CFGFUN(assign, const char *workspace, bool is_number); CFGFUN(no_focus); CFGFUN(ipc_socket, const char *path); diff --git a/include/data.h b/include/data.h index 7411ac20..632dfb4f 100644 --- a/include/data.h +++ b/include/data.h @@ -557,7 +557,8 @@ struct Assignment { A_COMMAND = (1 << 0), A_TO_WORKSPACE = (1 << 1), A_NO_FOCUS = (1 << 2), - A_TO_WORKSPACE_NUMBER = (1 << 3) + A_TO_WORKSPACE_NUMBER = (1 << 3), + A_TO_OUTPUT = (1 << 4) } type; /** the criteria to check if a window matches */ @@ -567,6 +568,7 @@ struct Assignment { union { char *command; char *workspace; + char *output; } dest; TAILQ_ENTRY(Assignment) diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 665b046a..8c89b3f9 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -141,7 +141,7 @@ state FOR_WINDOW_COMMAND: command = string -> call cfg_for_window($command) -# assign [→] workspace +# assign [→] [workspace | output] state ASSIGN: '[' -> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA @@ -149,6 +149,8 @@ state ASSIGN: state ASSIGN_WORKSPACE: '→' -> + 'output' + -> ASSIGN_OUTPUT 'workspace' -> 'number' @@ -156,6 +158,10 @@ state ASSIGN_WORKSPACE: workspace = string -> call cfg_assign($workspace, 0) +state ASSIGN_OUTPUT: + output = string + -> call cfg_assign_output($output) + state ASSIGN_WORKSPACE_NUMBER: number = string -> call cfg_assign($number, 1) diff --git a/src/config_directives.c b/src/config_directives.c index 376397e8..c981ff60 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -377,6 +377,20 @@ CFGFUN(color, const char *colorclass, const char *border, const char *background #undef APPLY_COLORS } +CFGFUN(assign_output, const char *output) { + if (match_is_empty(current_match)) { + ELOG("Match is empty, ignoring this assignment\n"); + return; + } + + DLOG("New assignment, using above criteria, to output \"%s\".\n", output); + Assignment *assignment = scalloc(1, sizeof(Assignment)); + match_copy(&(assignment->match), current_match); + assignment->type = A_TO_OUTPUT; + assignment->dest.output = sstrdup(output); + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); +} + CFGFUN(assign, const char *workspace, bool is_number) { if (match_is_empty(current_match)) { ELOG("Match is empty, ignoring this assignment\n"); diff --git a/src/manage.c b/src/manage.c index 8087d563..d12ce8d6 100644 --- a/src/manage.c +++ b/src/manage.c @@ -322,6 +322,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki } else nc = tree_open_con(NULL, cwindow); } + + if ((assignment = assignment_for(cwindow, A_TO_OUTPUT))) { + con_move_to_output_name(nc, assignment->dest.output, true); + } } else { /* M_BELOW inserts the new window as a child of the one which was * matched (e.g. dock areas) */ diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index 07bddfff..3c425467 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -203,8 +203,127 @@ my $content = get_ws($tmp); ok(@{$content->{nodes}} == 0, 'no tiling cons'); ok(@{$content->{floating_nodes}} == 1, 'one floating con'); -$window->destroy; +kill_all_windows; +exit_gracefully($pid); +##################################################################### +# test assignments to named outputs +##################################################################### +$config = < $class); + sync_with_i3; + is_num_children($ws, $expected_count, + "after: $expected_count containers on output $output"); +} + +cmd "workspace ws-0"; +open_in_output(0, 1); +my $focused = $x->input_focus; + +open_in_output(1, 1); +is($x->input_focus, $focused, 'focus remains on output fake-0'); + +open_in_output(2, 1); +is($x->input_focus, $focused, 'focus remains on output fake-0'); + +for my $i (1 .. 5){ + open_in_output(3, $i); + is($x->input_focus, $focused, 'focus remains on output fake-0'); +} + +# Check invalid output +$tmp = fresh_workspace; +open_special(wm_class => "special-4"); +sync_with_i3; +is_num_children($tmp, 1, 'window assigned to invalid output opened in current workspace'); +open_special(wm_class => "special-3"); +sync_with_i3; +is_num_children($tmp, 1, 'but window assigned to valid output did not'); + +kill_all_windows; +exit_gracefully($pid); + +##################################################################### +# Test assignments to outputs with relative names +##################################################################### +$config = < 'current'); +} +sync_with_i3; +is_num_children('left-top', 5, 'windows opened in current workspace'); + +is_num_children('right-top', 0, 'no children on right-top'); +open_special(wm_class => 'right'); +sync_with_i3; +is_num_children('right-top', 1, 'one child on right-top'); + +is_num_children('left-bottom', 0, 'no children on left-bottom'); +open_special(wm_class => 'down'); +sync_with_i3; +is_num_children('left-bottom', 1, 'one child on left-bottom'); + +cmd 'workspace right-bottom'; + +open_special(wm_class => 'up'); +sync_with_i3; +is_num_children('right-top', 2, 'two children on right-top'); + +open_special(wm_class => 'left'); +sync_with_i3; +is_num_children('left-bottom', 2, 'two children on left-bottom'); + +kill_all_windows; exit_gracefully($pid); ##################################################################### From 607e97e65190d1997048b510d72d30ebff1ca129 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 17 Sep 2017 15:25:00 +0200 Subject: [PATCH 074/187] ipc: rename COMMAND to RUN_COMMAND for consistency (#2956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other message types are verbs, only our first-ever message COMMAND wasn’t. While we’re here, also change the message type dictionary into a table with clickable links to the corresponding reply type. Authors of downstream IPC libraries are encouraged to keep the old name around so as to not break existing code, but mark it as deprecated. --- AnyEvent-I3/lib/AnyEvent/I3.pm | 7 +++-- docs/ipc | 57 ++++++++++++++-------------------- i3-msg/main.c | 8 +++-- i3bar/src/xcb.c | 4 +-- include/i3/ipc.h | 5 ++- src/ipc.c | 4 +-- src/main.c | 6 ++-- 7 files changed, 44 insertions(+), 47 deletions(-) diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm index 8598f850..6850b267 100644 --- a/AnyEvent-I3/lib/AnyEvent/I3.pm +++ b/AnyEvent-I3/lib/AnyEvent/I3.pm @@ -88,6 +88,7 @@ use base 'Exporter'; our @EXPORT = qw(i3); +use constant TYPE_RUN_COMMAND => 0; use constant TYPE_COMMAND => 0; use constant TYPE_GET_WORKSPACES => 1; use constant TYPE_SUBSCRIBE => 2; @@ -100,7 +101,7 @@ use constant TYPE_GET_BINDING_MODES => 8; use constant TYPE_GET_CONFIG => 9; our %EXPORT_TAGS = ( 'all' => [ - qw(i3 TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS + qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION TYPE_GET_BINDING_MODES TYPE_GET_CONFIG) ] ); @@ -322,7 +323,7 @@ Sends a message of the specified C to i3, possibly containing the data structure C (or C, encoded as utf8, if C is a scalar), if specified. - my $reply = $i3->message(TYPE_COMMAND, "reload")->recv; + my $reply = $i3->message(TYPE_RUN_COMMAND, "reload")->recv; if ($reply->{success}) { say "Configuration successfully reloaded"; } @@ -532,7 +533,7 @@ sub command { $self->_ensure_connection; - $self->message(TYPE_COMMAND, $content) + $self->message(TYPE_RUN_COMMAND, $content) } =head1 AUTHOR diff --git a/docs/ipc b/docs/ipc index ab2997fd..e0ddbf79 100644 --- a/docs/ipc +++ b/docs/ipc @@ -50,38 +50,20 @@ The magic string currently is "i3-ipc" and will only be changed when a change in the IPC API is done which breaks compatibility (we hope that we don’t need to do that). -Currently implemented message types are the following: - -COMMAND (0):: - The payload of the message is a command for i3 (like the commands you - can bind to keys in the configuration file) and will be executed - directly after receiving it. -GET_WORKSPACES (1):: - Gets the current workspaces. The reply will be a JSON-encoded list of - workspaces (see the reply section). -SUBSCRIBE (2):: - Subscribes your connection to certain events. See <> for a - description of this message and the concept of events. -GET_OUTPUTS (3):: - Gets the current outputs. The reply will be a JSON-encoded list of outputs - (see the reply section). -GET_TREE (4):: - Gets the layout tree. i3 uses a tree as data structure which includes - every container. The reply will be the JSON-encoded tree (see the reply - section). -GET_MARKS (5):: - Gets a list of marks (identifiers for containers to easily jump to them - later). The reply will be a JSON-encoded list of window marks (see - reply section). -GET_BAR_CONFIG (6):: - Gets the configuration (as JSON map) of the workspace bar with the - given ID. If no ID is provided, an array with all configured bar IDs is - returned instead. -GET_VERSION (7):: - Gets the version of i3. The reply will be a JSON-encoded dictionary - with the major, minor, patch and human-readable version. -GET_BINDING_MODES (8):: - Gets a list of currently configured binding modes. +.Currently implemented message types +[options="header",cols="^10%,^20%,^20%,^50%"] +|====================================================== +| Type (numeric) | Type (name) | Reply type | Purpose +| 0 | +RUN_COMMAND+ | <<_command_reply,COMMAND>> | Run the payload as an i3 command (like the commands you can bind to keys). +| 1 | +GET_WORKSPACES+ | <<_workspaces_reply,WORKSPACES>> | Get the list of current workspaces. +| 2 | +SUBSCRIBE+ | <<_subscribe_reply,SUBSCRIBE>> | Subscribe this IPC connection to the event types specified in the message payload. See <>. +| 3 | +GET_OUTPUTS+ | <<_outputs_reply,OUTPUTS>> | Get the list of current outputs. +| 4 | +GET_TREE+ | <<_tree_reply,TREE>> | Get the i3 layout tree. +| 5 | +GET_MARKS+ | <<_marks_reply,MARKS>> | Gets the names of all currently set marks. +| 6 | +GET_BAR_CONFIG+ | <<_bar_config_reply,BAR_CONFIG>> | Gets the specified bar configuration or the names of all bar configurations if payload is empty. +| 7 | +GET_VERSION+ | <<_version_reply,VERSION>> | Gets the i3 version. +| 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes. +|====================================================== So, a typical message could look like this: -------------------------------------------------- @@ -124,7 +106,7 @@ payload. The following reply types are implemented: COMMAND (0):: - Confirmation/Error code for the COMMAND message. + Confirmation/Error code for the RUN_COMMAND message. WORKSPACES (1):: Reply to the GET_WORKSPACES message. SUBSCRIBE (2):: @@ -142,6 +124,7 @@ VERSION (7):: BINDING_MODES (8):: Reply to the GET_BINDING_MODES message. +[[_command_reply]] === COMMAND reply The reply consists of a list of serialized maps for each command that was @@ -153,6 +136,7 @@ human-readable error message in the property +error (string)+. [{ "success": true }] ------------------- +[[_workspaces_reply]] === WORKSPACES reply The reply consists of a serialized list of workspaces. Each workspace has the @@ -212,6 +196,7 @@ output (string):: ] ------------------- +[[_subscribe_reply]] === SUBSCRIBE reply The reply consists of a single serialized map. The only property is @@ -223,6 +208,7 @@ default) or whether a JSON parse error occurred. { "success": true } ------------------- +[[_outputs_reply]] === OUTPUTS reply The reply consists of a serialized list of outputs. Each output has the @@ -269,6 +255,7 @@ rect (map):: ] ------------------- +[[_tree_reply]] === TREE reply The reply consists of a serialized tree. Each node in the tree (representing @@ -481,6 +468,7 @@ JSON dump: } ------------------------ +[[_marks_reply]] === MARKS reply The reply consists of a single array of strings for each container that has a @@ -489,6 +477,7 @@ The order of that array is undefined. If no window has a mark the response will be the empty array []. +[[_bar_config_reply]] === BAR_CONFIG reply This can be used by third-party workspace bars (especially i3bar, but others @@ -588,6 +577,7 @@ binding_mode_text/binding_mode_bg/binding_mode_border:: } -------------- +[[_version_reply]] === VERSION reply The reply consists of a single JSON dictionary with the following keys: @@ -620,6 +610,7 @@ loaded_config_file_name (string):: } ------------------- +[[_binding_modes_reply]] === BINDING_MODES reply The reply consists of an array of all currently configured binding modes. diff --git a/i3-msg/main.c b/i3-msg/main.c index 1a172789..8907a6f7 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -167,7 +167,7 @@ int main(int argc, char *argv[]) { else socket_path = NULL; int o, option_index = 0; - uint32_t message_type = I3_IPC_MESSAGE_TYPE_COMMAND; + uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND; char *payload = NULL; bool quiet = false; @@ -188,7 +188,9 @@ int main(int argc, char *argv[]) { socket_path = sstrdup(optarg); } else if (o == 't') { if (strcasecmp(optarg, "command") == 0) { - message_type = I3_IPC_MESSAGE_TYPE_COMMAND; + message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND; + } else if (strcasecmp(optarg, "run_command") == 0) { + message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND; } else if (strcasecmp(optarg, "get_workspaces") == 0) { message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES; } else if (strcasecmp(optarg, "get_outputs") == 0) { @@ -207,7 +209,7 @@ int main(int argc, char *argv[]) { message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG; } else { printf("Unknown message type\n"); - printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config\n"); + printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config\n"); exit(EXIT_FAILURE); } } else if (o == 'q') { diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 2ba446b1..462184cb 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -522,7 +522,7 @@ void handle_button(xcb_button_press_event_t *event) { if (binding->input_code != event->detail) continue; - i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, binding->command); + i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, binding->command); return; } @@ -603,7 +603,7 @@ void handle_button(xcb_button_press_event_t *event) { buffer[outpos] = utf8_name[inpos]; } buffer[outpos] = '"'; - i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer); + i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer); free(buffer); } diff --git a/include/i3/ipc.h b/include/i3/ipc.h index e3891454..993a2a24 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -27,9 +27,12 @@ typedef struct i3_ipc_header { /** Never change this, only on major IPC breakage (don’t do that) */ #define I3_IPC_MAGIC "i3-ipc" -/** The payload of the message will be interpreted as a command */ +/** Deprecated: use I3_IPC_MESSAGE_TYPE_RUN_COMMAND */ #define I3_IPC_MESSAGE_TYPE_COMMAND 0 +/** The payload of the message will be interpreted as a command */ +#define I3_IPC_MESSAGE_TYPE_RUN_COMMAND 0 + /** Requests the current workspaces from i3 */ #define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1 diff --git a/src/ipc.c b/src/ipc.c index 274f6010..dc953adc 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -113,7 +113,7 @@ void ipc_shutdown(shutdown_reason_t reason) { * or not (at the moment, always returns true). * */ -IPC_HANDLER(command) { +IPC_HANDLER(run_command) { /* To get a properly terminated buffer, we copy * message_size bytes out of the buffer */ char *command = scalloc(message_size + 1, 1); @@ -1121,7 +1121,7 @@ IPC_HANDLER(get_config) { /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ handler_t handlers[10] = { - handle_command, + handle_run_command, handle_get_workspaces, handle_subscribe, handle_get_outputs, diff --git a/src/main.c b/src/main.c index 44e4517e..f651ad6e 100644 --- a/src/main.c +++ b/src/main.c @@ -418,7 +418,7 @@ int main(int argc, char *argv[]) { if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) err(EXIT_FAILURE, "Could not connect to i3"); - if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_COMMAND, + if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_RUN_COMMAND, (uint8_t *)payload) == -1) err(EXIT_FAILURE, "IPC: write()"); FREE(payload); @@ -432,8 +432,8 @@ int main(int argc, char *argv[]) { err(EXIT_FAILURE, "IPC: read()"); return 1; } - if (reply_type != I3_IPC_MESSAGE_TYPE_COMMAND) - errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_MESSAGE_TYPE_COMMAND); + if (reply_type != I3_IPC_REPLY_TYPE_COMMAND) + errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_REPLY_TYPE_COMMAND); printf("%.*s\n", reply_length, reply); FREE(reply); return 0; From 54a7e559ed8fc24e8dd2a0b0a6a39edf4c741537 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 18 Sep 2017 23:03:54 +1200 Subject: [PATCH 075/187] Migrate tooling to ExtUtils::MakeMaker (#2963) --- AnyEvent-I3/MANIFEST | 11 +--- AnyEvent-I3/MANIFEST.SKIP | 2 + AnyEvent-I3/Makefile.PL | 107 +++++++++++++++++++++++++++++++++----- 3 files changed, 98 insertions(+), 22 deletions(-) diff --git a/AnyEvent-I3/MANIFEST b/AnyEvent-I3/MANIFEST index 34c8a8fb..8a166929 100644 --- a/AnyEvent-I3/MANIFEST +++ b/AnyEvent-I3/MANIFEST @@ -1,17 +1,8 @@ Changes -inc/Module/Install.pm -inc/Module/Install/Base.pm -inc/Module/Install/Can.pm -inc/Module/Install/Fetch.pm -inc/Module/Install/Makefile.pm -inc/Module/Install/Metadata.pm -inc/Module/Install/Win32.pm -inc/Module/Install/WriteAll.pm lib/AnyEvent/I3.pm Makefile.PL -MANIFEST +MANIFEST This list of files MANIFEST.SKIP -META.yml README t/00-load.t t/01-workspaces.t diff --git a/AnyEvent-I3/MANIFEST.SKIP b/AnyEvent-I3/MANIFEST.SKIP index 01bee91f..18e9d9df 100644 --- a/AnyEvent-I3/MANIFEST.SKIP +++ b/AnyEvent-I3/MANIFEST.SKIP @@ -9,3 +9,5 @@ Build.bat \.tar\.gz$ ^pod2htm(.*).tmp$ ^AnyEvent-I3- +^MYMETA.* +^MANIFEST\.bak diff --git a/AnyEvent-I3/Makefile.PL b/AnyEvent-I3/Makefile.PL index 5d2ab32e..29a442f4 100644 --- a/AnyEvent-I3/Makefile.PL +++ b/AnyEvent-I3/Makefile.PL @@ -1,17 +1,100 @@ -use lib '.'; -use inc::Module::Install; +use strict; +use warnings; -name 'AnyEvent-I3'; -all_from 'lib/AnyEvent/I3.pm'; -author 'Michael Stapelberg'; +use 5.006; +use ExtUtils::MakeMaker; -requires 'AnyEvent'; -requires 'AnyEvent::Handle'; -requires 'AnyEvent::Socket'; -requires 'JSON::XS'; - -if ($^O eq 'MSWin32') { +if ( $^O eq 'MSWin32' ) { die "AnyEvent::I3 cannot be used on win32 (unix sockets are missing)"; } -WriteAll; +my %meta = ( + name => 'AnyEvent-I3', + author => 'Michael Stapelberg, C<< >>', + license => ['perl'], + 'meta-spec' => { version => 2 }, + resources => { + repository => { + url => 'git://github.com/i3/i3', + web => 'https://github.com/i3/i3', + type => 'git', + }, + bugtracker => { + web => 'https://github.com/i3/i3/issues', + }, + homepage => 'https://i3wm.org/', + license => ['http://dev.perl.org/licenses'], + }, +); + +my %requirements = ( + configure_requires => { + 'ExtUtils::MakeMaker' => 6.36, + }, + build_requires => { + 'ExtUtils::MakeMaker' => 6.36 + }, + runtime_requires => { + 'AnyEvent' => 0, + 'AnyEvent::Handle' => 0, + 'AnyEvent::Socket' => 0, + 'JSON::XS' => 0, + }, + test_requires => { + 'Test::More' => 0.80, + }, +); + +my %merged_requirements = ( + 'ExtUtils::MakeMaker' => 0, + 'AnyEvent' => 0, + 'AnyEvent::Handle' => 0, + 'AnyEvent::Socket' => 0, + 'JSON::XS' => 0, + 'Test::More' => 0.80, +); + +$meta{prereqs}{configure}{requires} = $requirements{configure_requires}; +$meta{prereqs}{build}{requires} = $requirements{build_requires}; +$meta{prereqs}{runtime}{requires} = $requirements{runtime_requires}; +$meta{prereqs}{test}{requires} = $requirements{test_requires}; + +my %MM_Args = ( + AUTHOR => 'Michael Stapelberg', + NAME => 'AnyEvent::I3', + DISTNAME => 'AnyEvent-I3', + EXE_FILES => [], + MIN_PERL_VERSION => '5.006', + VERSION_FROM => 'lib/AnyEvent/I3.pm', + ABSTRACT_FROM => 'lib/AnyEvent/I3.pm', + test => { + TESTS => 't/*.t', + }, +); + +sub is_eumm { + eval { ExtUtils::MakeMaker->VERSION( $_[0] ) }; +} + +is_eumm(6.30) and $MM_Args{LICENSE} = $meta{license}[0]; +is_eumm(6.47_01) or delete $MM_Args{MIN_PERL_VERSION}; +is_eumm(6.52) + and $MM_Args{CONFIGURE_REQUIRES} = $requirements{configure_requires}; + +is_eumm(6.57_02) and !is_eumm(6.57_07) and $MM_Args{NO_MYMETA} = 1; + +if ( is_eumm(6.63_03) ) { + %MM_Args = ( + %MM_Args, + TEST_REQUIRES => $requirements{test_requires}, + BUILD_REQUIRES => $requirements{build_requires}, + PREREQ_PM => $requirements{runtime_requires}, + ); +} +else { + $MM_Args{PREREQ_PM} = \%merged_requirements; +} +unless ( -f 'META.yml' ) { + $MM_Args{META_ADD} = \%meta; +} +WriteMakefile(%MM_Args); From 39415fe16d96099015487827a763bc63c047dabe Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 18 Sep 2017 16:36:34 +0200 Subject: [PATCH 076/187] docs/ipc: "urgent": complete the list of container types (#2967) Thanks chressie! --- docs/ipc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/ipc b/docs/ipc index e0ddbf79..75ea9179 100644 --- a/docs/ipc +++ b/docs/ipc @@ -322,7 +322,10 @@ window (integer):: containers. This ID corresponds to what xwininfo(1) and other X11-related tools display (usually in hex). urgent (bool):: - Whether this container (window or workspace) has the urgency hint set. + Whether this container (window, split container, floating container or + workspace) has the urgency hint set, directly or indirectly. All parent + containers up until the workspace container will be marked urgent if they + have at least one urgent child. focused (bool):: Whether this container is currently focused. focus (array of integer):: From d1296cd1cfe6b47afd4fea563027629349c76db2 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 18 Sep 2017 16:36:57 +0200 Subject: [PATCH 077/187] =?UTF-8?q?Bugfix:=20don=E2=80=99t=20invalidate=20?= =?UTF-8?q?layout=20upon=20invalid=20'layout=20toggle'=20params=20(#2965)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #2903 --- src/con.c | 4 +++- testcases/t/292-regress-layout-toggle.t | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 testcases/t/292-regress-layout-toggle.t diff --git a/src/con.c b/src/con.c index a663db31..d346b9f4 100644 --- a/src/con.c +++ b/src/con.c @@ -1841,7 +1841,9 @@ void con_toggle_layout(Con *con, const char *toggle_mode) { } } - con_set_layout(con, new_layout); + if (new_layout != L_DEFAULT) { + con_set_layout(con, new_layout); + } } else if (strcasecmp(toggle_mode, "all") == 0 || strcasecmp(toggle_mode, "default") == 0) { if (parent->layout == L_STACKED) con_set_layout(con, L_TABBED); diff --git a/testcases/t/292-regress-layout-toggle.t b/testcases/t/292-regress-layout-toggle.t new file mode 100644 index 00000000..a8fd3a6d --- /dev/null +++ b/testcases/t/292-regress-layout-toggle.t @@ -0,0 +1,29 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Regression test: verify layout toggle with invalid parameters does not set +# layout to L_DEFAULT, which crashes i3 upon the next IPC message. +# Ticket: #2903 +# Bug still in: 4.14-87-g607e97e6 +use i3test; + +cmd 'layout toggle 1337 1337'; + +fresh_workspace; + +does_i3_live; + +done_testing; From e48441ecbdee4dae030c2e89a1c8836dd5a31c8a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 18 Sep 2017 16:37:34 +0200 Subject: [PATCH 078/187] Bugfix: avert crash by fixing focus when creating output containers (#2966) fixes #2854 --- src/randr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/randr.c b/src/randr.c index 88cad7c5..19c40577 100644 --- a/src/randr.c +++ b/src/randr.c @@ -380,6 +380,10 @@ void output_init_con(Output *output) { FREE(name); DLOG("attaching\n"); con_attach(bottomdock, con, false); + + /* Change focus to the content container */ + TAILQ_REMOVE(&(con->focus_head), content, focused); + TAILQ_INSERT_HEAD(&(con->focus_head), content, focused); } /* From 8e5731bde804a1361f61dd89191a0e31a4e79453 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 18 Sep 2017 17:15:28 +0200 Subject: [PATCH 079/187] =?UTF-8?q?ipc:=20document=20how=20to=20detect=20i?= =?UTF-8?q?3=E2=80=99s=20byte=20order=20in=20memory-safe=20languages=20(#2?= =?UTF-8?q?961)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to issue #2958 --- docs/ipc | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/docs/ipc b/docs/ipc index 75ea9179..4c2c778a 100644 --- a/docs/ipc +++ b/docs/ipc @@ -883,3 +883,63 @@ Rust:: * https://github.com/tmerr/i3ipc-rs OCaml:: * https://github.com/Armael/ocaml-i3ipc + +== Appendix A: Detecting byte order in memory-safe languages + +Some programming languages such as Go don’t offer a way to serialize data in the +native byte order of the machine they’re running on without resorting to tricks +involving the +unsafe+ package. + +The following technique can be used (and will not be broken by changes to i3) to +detect the byte order i3 is using: + +1. The byte order dependent fields of an IPC message are message type and + payload length. + + * The message type +RUN_COMMAND+ (0) is the same in big and little endian, so + we can use it in either byte order to elicit a reply from i3. + + * The payload length 65536 + 256 (+0x00 01 01 00+) is the same in big and + little endian, and also small enough to not worry about memory allocations + of that size. We must use payloads of length 65536 + 256 in every message + we send, so that i3 will be able to read the entire message regardless of + the byte order it uses. + +2. Send a big endian encoded message of type +SUBSCRIBE+ (2) with payload `[]` + followed by 65536 + 256 - 2 +SPACE+ (ASCII 0x20) bytes. + + * If i3 is running in big endian, this message is treated as a noop, + resulting in a +SUBSCRIBE+ reply with payload `{"success":true}` + footnote:[A small payload is important: that way, we circumvent dealing + with UNIX domain socket buffer sizes, whose size depends on the + implementation/operating system. Exhausting such a buffer results in an i3 + deadlock unless you concurrently read and write, which — depending on the + programming language — makes the technique much more complicated.]. + + * If i3 is running in little endian, this message is read in its entirety due + to the byte order independent payload length, then + https://github.com/i3/i3/blob/d726d09d496577d1c337a4b97486f2c9fbc914f1/src/ipc.c#L1188[silently + discarded] due to the unknown message type. + +3. Send a byte order independent message, i.e. type +RUN_COMMAND+ (0) with + payload +nop byte order detection. padding:+, padded to 65536 + 256 bytes + with +a+ (ASCII 0x61) bytes. i3 will reply to this message with a reply of + type +COMMAND+ (0). + + * The human-readable prefix is in there to not confuse readers of the i3 log. + + * This messages serves as a synchronization primitive so that we know whether + i3 discarded the +SUBSCRIBE+ message or didn’t answer it yet. + +4. Receive a message header from i3, decoding the message type as big endian. + + * If the message’s reply type is +COMMAND+ (0), i3 is running in little + endian (because the +SUBSCRIBE+ message was discarded). Decode the message + payload length as little endian, receive the message payload. + + * If the message’s reply type is anything else, i3 is running in big endian + (because our big endian encoded +SUBSCRIBE+ message was answered). Decode + the message payload length in big endian, receive the message + payload. Then, receive the pending +COMMAND+ message reply in big endian. + +5. From here on out, send/receive all messages using the detected byte order. From a3a7d04a43b7eb882fe647363a5bd35a4627e3f2 Mon Sep 17 00:00:00 2001 From: Orestis Date: Mon, 18 Sep 2017 19:02:38 +0300 Subject: [PATCH 080/187] 291-swap.t: make tests fail less (#2964) --- testcases/t/291-swap.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testcases/t/291-swap.t b/testcases/t/291-swap.t index f9ac3b6a..9bbd3795 100644 --- a/testcases/t/291-swap.t +++ b/testcases/t/291-swap.t @@ -226,6 +226,7 @@ $expected_focus = get_focused($ws1); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); $B->fullscreen(1); +sync_with_i3; cmd '[con_mark=B] swap container with mark A'; @@ -263,6 +264,7 @@ $expected_focus = get_focused($ws1); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); $B->fullscreen(1); +sync_with_i3; cmd '[con_mark=B] swap container with mark A'; From 755b22327807593fd184c4dae46b197848a74452 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 19 Sep 2017 14:33:51 +0000 Subject: [PATCH 081/187] fake_outputs: Use %n format specifier instead of sprintf fake_outputs_init used a sprintf invocation with a throw-away buffer to estimate how many characters the sscanf invocation consumed. This was unnecessary, and also potentially incorrect, as differences between the read and formatted strings (such as leading zeros) could lead to fake_outputs_init to lose its track. Instead, use the %n format specifier which allows saving the number of characters consumed by sscanf so far. %n is part of C99. --- src/fake_outputs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 6639b361..447409d2 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -33,10 +33,10 @@ static Output *get_screen_at(unsigned int x, unsigned int y) { * */ void fake_outputs_init(const char *output_spec) { - char useless_buffer[1024]; const char *walk = output_spec; unsigned int x, y, width, height; - while (sscanf(walk, "%ux%u+%u+%u", &width, &height, &x, &y) == 4) { + int chars_consumed; + while (sscanf(walk, "%ux%u+%u+%u%n", &width, &height, &x, &y, &chars_consumed) == 4) { DLOG("Parsed output as width = %u, height = %u at (%u, %u)\n", width, height, x, y); Output *new_output = get_screen_at(x, y); @@ -68,8 +68,7 @@ void fake_outputs_init(const char *output_spec) { num_screens++; } - /* Figure out how long the input was to skip it */ - walk += sprintf(useless_buffer, "%ux%u+%u+%u", width, height, x, y) + 1; + walk += chars_consumed + 1; } if (num_screens == 0) { From 19b00346e5e805cb88ec24c1d29bbd1d292eec14 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 19 Sep 2017 14:37:35 +0000 Subject: [PATCH 082/187] fake_outputs: Don't read past the end of string fake_outputs_init would unconditionally increase the string read pointer variable (walk) by one character more than the number of characters that have been read, to skip past the character delimiting records (a comma). However, when the input string was not terminated by a comma, it would cause the function to read past the null terminator instead. Avoid this by explicitly checking for the expected delimiter. --- src/fake_outputs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 447409d2..64c3e20c 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -68,7 +68,9 @@ void fake_outputs_init(const char *output_spec) { num_screens++; } - walk += chars_consumed + 1; + walk += chars_consumed; + if (*walk == ',') + walk++; } if (num_screens == 0) { From caae4db0bc6516b08a2f138c5845c34053e50947 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 19 Sep 2017 14:42:13 +0000 Subject: [PATCH 083/187] fake_outputs: Allow designating a fake output as primary Allow appending 'P' to the fake output specification to set the created output's "primary" flag, to allow writing test cases that depend on the presence of a primary output. --- src/fake_outputs.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 64c3e20c..39cbd7bb 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -28,8 +28,9 @@ static Output *get_screen_at(unsigned int x, unsigned int y) { /* * Creates outputs according to the given specification. * The specification must be in the format wxh+x+y, for example 1024x768+0+0, + * optionally followed by 'P' to indicate a primary output, * with multiple outputs separated by commas: - * 1900x1200+0+0,1280x1024+1900+0 + * 1900x1200+0+0P,1280x1024+1900+0 * */ void fake_outputs_init(const char *output_spec) { @@ -37,8 +38,17 @@ void fake_outputs_init(const char *output_spec) { unsigned int x, y, width, height; int chars_consumed; while (sscanf(walk, "%ux%u+%u+%u%n", &width, &height, &x, &y, &chars_consumed) == 4) { - DLOG("Parsed output as width = %u, height = %u at (%u, %u)\n", - width, height, x, y); + walk += chars_consumed; + bool primary = false; + if (*walk == 'P') { + primary = true; + walk++; + } + if (*walk == ',') + walk++; /* Skip delimiter */ + DLOG("Parsed output as width = %u, height = %u at (%u, %u)%s\n", + width, height, x, y, primary ? " (primary)" : ""); + Output *new_output = get_screen_at(x, y); if (new_output != NULL) { DLOG("Re-used old output %p\n", new_output); @@ -67,10 +77,7 @@ void fake_outputs_init(const char *output_spec) { init_ws_for_output(new_output, output_get_content(new_output->con)); num_screens++; } - - walk += chars_consumed; - if (*walk == ',') - walk++; + new_output->primary = primary; } if (num_screens == 0) { From 760b2e1b4a2452c701ce06b4e94d00e74e778d59 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 19 Sep 2017 14:46:27 +0000 Subject: [PATCH 084/187] Do not canonicalize special output names canonicalize_output_name allowed the "primary" special output name to be canonicalized, thus converting it to the name of whatever output was the primary output at the time. This caused settings (specifically, i3bar output and tray_output settings) to be stored as specific output names, instead of the intended special names whose referred output may change as the system's configuration (i.e. current primary output) changes. Add a check to canonicalize_output_name to return the name as-is if it is the special name "primary". --- src/ipc.c | 4 +++ testcases/t/538-i3bar-primary-output.t | 40 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 testcases/t/538-i3bar-primary-output.t diff --git a/src/ipc.c b/src/ipc.c index dc953adc..759665fe 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -580,6 +580,10 @@ static void dump_bar_bindings(yajl_gen gen, Barconfig *config) { } static char *canonicalize_output_name(char *name) { + /* Do not canonicalize special output names. */ + if (strcasecmp(name, "primary") == 0) { + return name; + } Output *output = get_output_by_name(name, false); return output ? output_primary_name(output) : name; } diff --git a/testcases/t/538-i3bar-primary-output.t b/testcases/t/538-i3bar-primary-output.t new file mode 100644 index 00000000..67fd1b6c --- /dev/null +++ b/testcases/t/538-i3bar-primary-output.t @@ -0,0 +1,40 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests that i3bars configured to use the primary output do not have +# their output names canonicalized to something other than "primary". +# Ticket: #2948 +# Bug still in: 4.14-93-ga3a7d04a +use i3test i3_config => <get_bar_config()->recv; +is(@$bars, 1, 'one bar configured'); + +my $bar_id = shift @$bars; + +my $bar_config = i3->get_bar_config($bar_id)->recv; +is_deeply($bar_config->{outputs}, [ "primary" ], 'bar_config output is primary'); + +done_testing; From 536f60e230713fa131cdeeebf80bc5518c62eeee Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 15 Sep 2017 01:59:50 +0000 Subject: [PATCH 085/187] docs/userguide: Update description of focus wrapping Focus wrapping applies to all kinds of containers, not just tabbed/stacked ones. --- docs/userguide | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/userguide b/docs/userguide index cc9b5a01..7064dcef 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1039,11 +1039,12 @@ popup_during_fullscreen smart === Focus wrapping -When being in a tabbed or stacked container, the first container will be -focused when you use +focus down+ on the last container -- the focus wraps. If -however there is another stacked/tabbed container in that direction, focus will -be set on that container. This is the default behavior so you can navigate to -all your windows without having to use +focus parent+. +When in a container with several windows or child containers, the opposite +window will be focused when trying to move the focus over the edge of a +container (and there are no other containers in that direction) -- the focus +wraps. If however there is another window or container in that direction, focus +will be set on that window or container. This is the default behavior so you +can navigate to all your windows without having to use +focus parent+. If you want the focus to *always* wrap and you are aware of using +focus parent+ to switch to different containers, you can use the From bf7aeced308c07b9d324c7d85a39d35618343a18 Mon Sep 17 00:00:00 2001 From: "Martin T. H. Sandsmark" Date: Tue, 27 Dec 2016 19:00:09 +0100 Subject: [PATCH 086/187] Don't put new floating windows on top unless they're focused --- src/floating.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/floating.c b/src/floating.c index f2994339..f0499e60 100644 --- a/src/floating.c +++ b/src/floating.c @@ -196,7 +196,11 @@ void floating_enable(Con *con, bool automatic) { /* We insert nc already, even though its rect is not yet calculated. This * is necessary because otherwise the workspace might be empty (and get * closed in tree_close_internal()) even though it’s not. */ - TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows); + if (set_focus) { + TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows); + } else { + TAILQ_INSERT_HEAD(&(ws->floating_head), nc, floating_windows); + } TAILQ_INSERT_TAIL(&(ws->focus_head), nc, focused); /* check if the parent container is empty and close it if so */ From 28f7e14650882d89fae2eee78291eeec8dd4e8fd Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 15 Sep 2017 02:57:55 +0000 Subject: [PATCH 087/187] Add "focus_wrapping" option Fixes #2352. --- docs/userguide | 28 ++++++++++--- include/config_directives.h | 1 + include/configuration.h | 8 ++++ parser-specs/config.spec | 6 +++ src/config.c | 2 + src/config_directives.c | 4 ++ src/tree.c | 2 +- testcases/t/201-config-parser.t | 1 + testcases/t/539-disable_focus_wrapping.t | 51 ++++++++++++++++++++++++ 9 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 testcases/t/539-disable_focus_wrapping.t diff --git a/docs/userguide b/docs/userguide index 7064dcef..fdfb121b 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1039,12 +1039,28 @@ popup_during_fullscreen smart === Focus wrapping -When in a container with several windows or child containers, the opposite -window will be focused when trying to move the focus over the edge of a -container (and there are no other containers in that direction) -- the focus -wraps. If however there is another window or container in that direction, focus -will be set on that window or container. This is the default behavior so you -can navigate to all your windows without having to use +focus parent+. +By default, when in a container with several windows or child containers, the +opposite window will be focused when trying to move the focus over the edge of +a container (and there are no other containers in that direction) -- the focus +wraps. + +If desired, you can disable this behavior using the +focus_wrapping+ +configuration directive: + +*Syntax*: +--------------------- +focus_wrapping yes|no +--------------------- + +*Example*: +----------------- +focus_wrapping no +----------------- + +By default, focus wrapping does not occur if there is another window or +container in the specified direction, and focus will instead be set on that +window or container. This is the default behavior so you can navigate to all +your windows without having to use +focus parent+. If you want the focus to *always* wrap and you are aware of using +focus parent+ to switch to different containers, you can use the diff --git a/include/config_directives.h b/include/config_directives.h index b729e728..66defa8f 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -49,6 +49,7 @@ CFGFUN(workspace_layout, const char *layout); CFGFUN(workspace_back_and_forth, const char *value); CFGFUN(focus_follows_mouse, const char *value); CFGFUN(mouse_warping, const char *value); +CFGFUN(focus_wrapping, const char *value); CFGFUN(force_focus_wrapping, const char *value); CFGFUN(force_xinerama, const char *value); CFGFUN(disable_randr15, const char *value); diff --git a/include/configuration.h b/include/configuration.h index 4f6e5ce8..33df2c2d 100644 --- a/include/configuration.h +++ b/include/configuration.h @@ -137,6 +137,14 @@ struct Config { * comes with i3. Thus, you can turn it off entirely. */ bool disable_workspace_bar; + /** When focus wrapping is enabled (the default), attempting to + * move focus past the edge of the screen (in other words, in a + * direction in which there are no more containers to focus) will + * cause the focus to wrap to the opposite edge of the current + * container. When it is disabled, nothing happens; the current + * focus is preserved. */ + bool focus_wrapping; + /** Think of the following layout: Horizontal workspace with a tabbed * con on the left of the screen and a terminal on the right of the * screen. You are in the second container in the tabbed container and diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 665b046a..3a10bbc1 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -36,6 +36,7 @@ state INITIAL: 'no_focus' -> NO_FOCUS 'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE 'mouse_warping' -> MOUSE_WARPING + 'focus_wrapping' -> FOCUS_WRAPPING 'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING 'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA 'disable_randr15', 'disable-randr15' -> DISABLE_RANDR15 @@ -203,6 +204,11 @@ state MOUSE_WARPING: value = 'none', 'output' -> call cfg_mouse_warping($value) +# focus_wrapping +state FOCUS_WRAPPING: + value = word + -> call cfg_focus_wrapping($value) + # force_focus_wrapping state FORCE_FOCUS_WRAPPING: value = word diff --git a/src/config.c b/src/config.c index 7e08b520..c8e9bd6b 100644 --- a/src/config.c +++ b/src/config.c @@ -227,6 +227,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, if (config.workspace_urgency_timer == 0) config.workspace_urgency_timer = 0.5; + config.focus_wrapping = true; + parse_configuration(override_configpath, true); if (reload) { diff --git a/src/config_directives.c b/src/config_directives.c index 376397e8..41d21dec 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -264,6 +264,10 @@ CFGFUN(disable_randr15, const char *value) { config.disable_randr15 = eval_boolstr(value); } +CFGFUN(focus_wrapping, const char *value) { + config.focus_wrapping = eval_boolstr(value); +} + CFGFUN(force_focus_wrapping, const char *value) { config.force_focus_wrapping = eval_boolstr(value); } diff --git a/src/tree.c b/src/tree.c index 7f466583..97c02798 100644 --- a/src/tree.c +++ b/src/tree.c @@ -675,7 +675,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) * */ void tree_next(char way, orientation_t orientation) { - _tree_next(focused, way, orientation, true); + _tree_next(focused, way, orientation, config.focus_wrapping); } /* diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index e8080a73..3e2c4297 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -470,6 +470,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: , ' no_focus focus_follows_mouse mouse_warping + focus_wrapping force_focus_wrapping force_xinerama force-xinerama diff --git a/testcases/t/539-disable_focus_wrapping.t b/testcases/t/539-disable_focus_wrapping.t new file mode 100644 index 00000000..8d2e8472 --- /dev/null +++ b/testcases/t/539-disable_focus_wrapping.t @@ -0,0 +1,51 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests that focus does not wrap when focus_wrapping is disabled in +# the configuration. +# Ticket: #2352 +# Bug still in: 4.14-72-g6411130c +use i3test i3_config => <input_focus, $win2->id, "Second window focused initially"); + cmd "focus $prev"; + is($x->input_focus, $win1->id, "First window focused"); + cmd "focus $prev"; + is($x->input_focus, $win1->id, "First window still focused"); + cmd "focus $next"; + is($x->input_focus, $win2->id, "Second window focused"); + cmd "focus $next"; + is($x->input_focus, $win2->id, "Second window still focused"); +} + +test_orientation('v', 'up', 'down'); +test_orientation('h', 'left', 'right'); + +done_testing; From 76214b04e841cddb7d1b1bcbe52561f323570cf9 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Fri, 22 Sep 2017 22:00:06 +0300 Subject: [PATCH 088/187] Raise floating window to top when it gets focus Applied for: 1. '[...] focus' for a floating container raises it to the top. 2. Focusing a window through a focus event raises it to the top. Fixes #2572 --- src/click.c | 2 -- src/con.c | 7 +++++++ src/handlers.c | 8 +++++--- testcases/t/135-floating-focus.t | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/click.c b/src/click.c index e5cdc8b2..78af8a03 100644 --- a/src/click.c +++ b/src/click.c @@ -262,8 +262,6 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod * We will skip handling events on floating cons in fullscreen mode */ Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL); if (floatingcon != NULL && fs != con) { - floating_raise_con(floatingcon); - /* 4: floating_modifier plus left mouse button drags */ if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) { floating_drag_window(floatingcon, event); diff --git a/src/con.c b/src/con.c index 29a326e7..df6a8437 100644 --- a/src/con.c +++ b/src/con.c @@ -243,6 +243,13 @@ void con_focus(Con *con) { workspace_update_urgent_flag(con_get_workspace(con)); ipc_send_window_event("urgent", con); } + + /* Focusing a container with a floating parent should raise it to the top. Since + * con_focus is called recursively for each parent we don't need to use + * con_inside_floating(). */ + if (con->type == CT_FLOATING_CON) { + floating_raise_con(con); + } } /* diff --git a/src/handlers.c b/src/handlers.c index 8d500fd9..1da42ec2 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1221,7 +1221,9 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } - if (focused_id == event->event) { + /* Floating windows should be refocused to ensure that they are on top of + * other windows. */ + if (focused_id == event->event && !con_inside_floating(con)) { DLOG("focus matches the currently focused window, not doing anything\n"); return; } @@ -1232,7 +1234,7 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } - DLOG("focus is different, updating decorations\n"); + DLOG("focus is different / refocusing floating window: updating decorations\n"); /* Get the currently focused workspace to check if the focus change also * involves changing workspaces. If so, we need to call workspace_show() to @@ -1244,7 +1246,7 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { con_focus(con); /* We update focused_id because we don’t need to set focus again */ focused_id = event->event; - x_push_changes(croot); + tree_render(); return; } diff --git a/testcases/t/135-floating-focus.t b/testcases/t/135-floating-focus.t index f23dabae..f21c0486 100644 --- a/testcases/t/135-floating-focus.t +++ b/testcases/t/135-floating-focus.t @@ -216,4 +216,28 @@ cmd 'focus child'; is($x->input_focus, $floating->id, 'floating window focused'); +############################################################################# +# 8: verify that focusing a floating window raises it to the top. +# This test can't verify that the floating container is visually on top, just +# that it is placed on the tail of the floating_head. +# See issue: 2572 +############################################################################# + +$tmp = fresh_workspace; + +$first = open_floating_window; +$second = open_floating_window; + +is($x->input_focus, $second->id, 'second floating window focused'); +my $ws = get_ws($tmp); +is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second->id, 'second on top'); +is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first->id, 'first behind'); + +cmd '[id=' . $first->id . '] focus'; + +is($x->input_focus, $first->id, 'first floating window focused'); +$ws = get_ws($tmp); +is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $first->id, 'first on top'); +is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $second->id, 'second behind'); + done_testing; From be9a2bb7f8ffac4c360b272cd5717bc158c0bb74 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Fri, 22 Sep 2017 19:22:48 +0300 Subject: [PATCH 089/187] Add con_exists function Checks the all_cons queue and returns true if a given con is found. --- include/con.h | 7 +++++++ src/con.c | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/include/con.h b/include/con.h index 69292411..b88ea354 100644 --- a/include/con.h +++ b/include/con.h @@ -165,6 +165,13 @@ Con *con_by_window_id(xcb_window_t window); */ Con *con_by_con_id(long target); +/** + * Returns true if the given container (still) exists. + * This can be used, e.g., to make sure a container hasn't been closed in the meantime. + * + */ +bool con_exists(Con *con); + /** * Returns the container with the given frame ID or NULL if no such container * exists. diff --git a/src/con.c b/src/con.c index 29a326e7..e0901443 100644 --- a/src/con.c +++ b/src/con.c @@ -597,6 +597,15 @@ Con *con_by_con_id(long target) { return NULL; } +/* + * Returns true if the given container (still) exists. + * This can be used, e.g., to make sure a container hasn't been closed in the meantime. + * + */ +bool con_exists(Con *con) { + return con_by_con_id((long)con) != NULL; +} + /* * Returns the container with the given frame ID or NULL if no such container * exists. From 414d23fb7a8b09598dbf679baa56ae269e8e35a2 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Fri, 22 Sep 2017 19:25:02 +0300 Subject: [PATCH 090/187] Check container existance during drag events This fixes a crash that occurs when disabling floating for a container while it is being moved or resized. @Deiz describes the problem: > It occurs because the command that disables floating runs before the event loop. So, the window is tiled, its floating parent is destroyed, but then a key event is handled which causes the position/size of the now-destroyed parent to be modified. Fixes #1627 --- src/floating.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/floating.c b/src/floating.c index f2994339..6a953132 100644 --- a/src/floating.c +++ b/src/floating.c @@ -531,6 +531,11 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { /* Drag the window */ drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event); + if (!con_exists(con)) { + DLOG("The container has been closed in the meantime.\n"); + return; + } + /* If the user cancelled, undo the changes. */ if (drag_result == DRAG_REVERT) floating_reposition(con, initial_rect); @@ -642,6 +647,11 @@ void floating_resize_window(Con *con, const bool proportional, drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, ¶ms); + if (!con_exists(con)) { + DLOG("The container has been closed in the meantime.\n"); + return; + } + /* If the user cancels, undo the resize */ if (drag_result == DRAG_REVERT) floating_reposition(con, initial_rect); @@ -739,12 +749,17 @@ static void xcb_drag_check_cb(EV_P_ ev_check *w, int revents) { if (last_motion_notify == NULL) return; - dragloop->callback( - dragloop->con, - &(dragloop->old_rect), - last_motion_notify->root_x, - last_motion_notify->root_y, - dragloop->extra); + /* Ensure that we are either dragging the resize handle (con is NULL) or that the + * container still exists. The latter might not be true, e.g., if the window closed + * for any reason while the user was dragging it. */ + if (!dragloop->con || con_exists(dragloop->con)) { + dragloop->callback( + dragloop->con, + &(dragloop->old_rect), + last_motion_notify->root_x, + last_motion_notify->root_y, + dragloop->extra); + } free(last_motion_notify); } From d968d39b27daa98848a28756eed678ec6b95cae9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 24 Sep 2017 10:19:07 +0200 Subject: [PATCH 091/187] Replace http:// with https:// where applicable The testcases will be updated automatically in a separate commit. --- .github/CONTRIBUTING.md | 6 +++--- .github/ISSUE_TEMPLATE.md | 2 +- AnyEvent-I3/Makefile.PL | 2 +- AnyEvent-I3/README | 6 +++--- AnyEvent-I3/lib/AnyEvent/I3.pm | 8 ++++---- DEPENDS | 26 +++++++++++++------------- contrib/per-workspace-layout.pl | 2 +- debian/control | 2 +- debian/copyright | 2 +- docs/asciidoc-git.conf | 6 +++--- docs/debugging | 16 ++++++++-------- docs/hacking-howto | 6 +++--- docs/i3-pod2html | 6 +++--- docs/i3bar-protocol | 2 +- docs/layout-saving | 8 ++++---- docs/refcard.html | 2 +- docs/testsuite | 10 +++++----- docs/userguide | 6 +++--- docs/wsbar | 2 +- etc/config | 2 +- etc/config.keycodes | 2 +- i3-dmenu-desktop | 10 +++++----- i3-dump-log/main.c | 2 +- i3bar/src/xcb.c | 2 +- include/ewmh.h | 6 +++--- include/libi3.h | 2 +- include/randr.h | 2 +- include/sd-daemon.h | 4 ++-- libi3/get_exe_path.c | 2 +- man/i3-msg.man | 2 +- man/i3.man | 2 +- man/i3bar.man | 2 +- pseudo-doc.doxygen | 2 +- src/bindings.c | 2 +- src/ewmh.c | 6 +++--- src/handlers.c | 4 ++-- src/main.c | 2 +- src/randr.c | 2 +- src/render.c | 8 ++++---- src/restore_layout.c | 2 +- src/sighandler.c | 2 +- src/tree.c | 2 +- src/window.c | 2 +- src/x.c | 2 +- testcases/lib/StartXServer.pm | 2 +- testcases/lib/StatusLine.pm | 2 +- testcases/lib/i3test.pm.in | 4 ++-- testcases/lib/i3test/XTEST.pm | 2 +- testcases/new-test | 6 +++--- travis/docs.sh | 4 ++-- 50 files changed, 108 insertions(+), 108 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c19ac81e..84644060 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,7 +6,7 @@ Note that bug reports and feature requests for related projects should be filed ## i3 bug reports and feature requests -1. Read the [debugging instructions](http://i3wm.org/docs/debugging.html). +1. Read the [debugging instructions](https://i3wm.org/docs/debugging.html). 2. Make sure you include a link to your logfile in your report (section 3). 3. Make sure you include the i3 version number in your report (section 1). 4. Please be aware that we cannot support compatibility issues with @@ -25,11 +25,11 @@ Note that bug reports and feature requests for related projects should be filed ”feature request” or ”enhancement” in its title. * Use the `next` branch for developing and sending your pull request. * Use `clang-format` to format your code. -* Run the [testsuite](http://i3wm.org/docs/testsuite.html) +* Run the [testsuite](https://i3wm.org/docs/testsuite.html) ## Finding something to do * Find a [reproducible bug](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Areproducible+label%3Abug+) from the issue tracker. These issues have been reviewed and confirmed by a project contributor. * Find an [accepted enhancement](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Aaccepted+label%3Aenhancement) from the issue tracker. These have been approved and are ok to start working on. -There's a very good [overview of the codebase](http://i3wm.org/docs/hacking-howto.html) available to get you started. +There's a very good [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index d2254676..a9cfbd47 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ Output of `i3 --moreversion 2>&- || i3 --version`: _REPLACE: i3 version output_ -URL to a logfile as per http://i3wm.org/docs/debugging.html: +URL to a logfile as per https://i3wm.org/docs/debugging.html: _REPLACE: URL to logfile_ diff --git a/AnyEvent-I3/Makefile.PL b/AnyEvent-I3/Makefile.PL index 29a442f4..cd727197 100644 --- a/AnyEvent-I3/Makefile.PL +++ b/AnyEvent-I3/Makefile.PL @@ -23,7 +23,7 @@ my %meta = ( web => 'https://github.com/i3/i3/issues', }, homepage => 'https://i3wm.org/', - license => ['http://dev.perl.org/licenses'], + license => ['https://dev.perl.org/licenses'], }, ); diff --git a/AnyEvent-I3/README b/AnyEvent-I3/README index 4658ba16..fe9fdb74 100644 --- a/AnyEvent-I3/README +++ b/AnyEvent-I3/README @@ -23,10 +23,10 @@ perldoc command. You can also look for information at: RT, CPAN's request tracker - http://rt.cpan.org/NoAuth/Bugs.html?Dist=AnyEvent-I3 + https://rt.cpan.org/NoAuth/Bugs.html?Dist=AnyEvent-I3 The i3 window manager website - http://i3.zekjur.net/ + https://i3wm.org LICENSE AND COPYRIGHT @@ -37,4 +37,4 @@ This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. -See http://dev.perl.org/licenses/ for more information. +See https://dev.perl.org/licenses/ for more information. diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm index 6850b267..08b1c0a7 100644 --- a/AnyEvent-I3/lib/AnyEvent/I3.pm +++ b/AnyEvent-I3/lib/AnyEvent/I3.pm @@ -544,7 +544,7 @@ Michael Stapelberg, C<< >> Please report any bugs or feature requests to C, or through the web interface at -L. I will be +L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. @@ -560,11 +560,11 @@ You can also look for information at: =item * RT: CPAN's request tracker -L +L =item * The i3 window manager website -L +L =back @@ -580,7 +580,7 @@ This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. -See http://dev.perl.org/licenses/ for more information. +See https://dev.perl.org/licenses/ for more information. =cut diff --git a/DEPENDS b/DEPENDS index 167827ff..1e26afa2 100644 --- a/DEPENDS +++ b/DEPENDS @@ -7,29 +7,29 @@ ┌──────────────┬────────┬────────┬───────────────────────────────────────────────────────────┐ │ dependency │ min. │ lkgv │ URL │ ├──────────────┼────────┼────────┼───────────────────────────────────────────────────────────┤ -│ pkg-config │ 0.25 │ 0.29 │ http://pkgconfig.freedesktop.org/ │ -│ libxcb │ 1.1.93 │ 1.12 │ http://xcb.freedesktop.org/dist/ │ -│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │ -│ xkbcommon │ 0.4.0 │ 0.6.1 │ http://xkbcommon.org/ │ -│ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ http://xkbcommon.org/ │ -│ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ http://xcb.freedesktop.org/dist/ │ -│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │ -│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ http://xcb.freedesktop.org/dist/ │ +│ pkg-config │ 0.25 │ 0.29 │ https://pkgconfig.freedesktop.org/ │ +│ libxcb │ 1.1.93 │ 1.12 │ https://xcb.freedesktop.org/dist/ │ +│ xcb-util │ 0.3.3 │ 0.4.1 │ https://xcb.freedesktop.org/dist/ │ +│ xkbcommon │ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │ +│ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │ +│ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ https://xcb.freedesktop.org/dist/ │ +│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ https://xcb.freedesktop.org/dist/ │ +│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ https://xcb.freedesktop.org/dist/ │ │ util-xrm⁴ │ 1.0.0 │ 1.0.0 │ https://github.com/Airblader/xcb-util-xrm │ │ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/ │ -│ yajl │ 2.0.1 │ 2.1.0 │ http://lloyd.github.com/yajl/ │ +│ yajl │ 2.0.1 │ 2.1.0 │ https://lloyd.github.com/yajl/ │ │ asciidoc │ 8.3.0 │ 8.6.9 │ http://www.methods.co.nz/asciidoc/ │ │ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │ │ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/ │ │ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │ -│ PCRE │ 8.12 │ 8.38 │ http://www.pcre.org/ │ -│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification │ +│ PCRE │ 8.12 │ 8.38 │ https://www.pcre.org/ │ +│ libsn¹ │ 0.10 │ 0.12 │ https://freedesktop.org/wiki/Software/startup-notification │ │ pango │ 1.30.0 | 1.40.1 │ http://www.pango.org/ │ -│ cairo │ 1.14.4 │ 1.14.6 │ http://cairographics.org/ │ +│ cairo │ 1.14.4 │ 1.14.6 │ https://cairographics.org/ │ └──────────────┴────────┴────────┴───────────────────────────────────────────────────────────┘ ¹ libsn = libstartup-notification ² Pod::Simple is a Perl module required for converting the testsuite - documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple + documentation to HTML. See https://michael.stapelberg.de/cpan/#Pod::Simple ³ xcb-util-cursor, to be precise. ⁴ Depending on your distribution, this might be considered part of xcb-util. diff --git a/contrib/per-workspace-layout.pl b/contrib/per-workspace-layout.pl index 9304b6fd..48590456 100644 --- a/contrib/per-workspace-layout.pl +++ b/contrib/per-workspace-layout.pl @@ -1,7 +1,7 @@ #!/usr/bin/env perl # vim:ts=4:sw=4:expandtab # © 2012 Michael Stapelberg -# Licensed under BSD license, see http://code.i3wm.org/i3/tree/LICENSE +# Licensed under BSD license, see https://github.com/i3/i3/blob/next/LICENSE # # Append this line to your i3 config file: # exec_always ~/per-workspace-layout.pl diff --git a/debian/control b/debian/control index 76a84a5a..46e35dfe 100644 --- a/debian/control +++ b/debian/control @@ -27,7 +27,7 @@ Build-Depends: debhelper (>= 9), libpango1.0-dev, libpod-simple-perl Standards-Version: 3.9.8 -Homepage: http://i3wm.org/ +Homepage: https://i3wm.org/ Package: i3 Architecture: any diff --git a/debian/copyright b/debian/copyright index f86d850f..f80edad8 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: i3 Upstream-Contact: Michael Stapelberg Source: https://i3wm.org/ diff --git a/docs/asciidoc-git.conf b/docs/asciidoc-git.conf index 36bdb6f5..08f28a5d 100644 --- a/docs/asciidoc-git.conf +++ b/docs/asciidoc-git.conf @@ -520,8 +520,8 @@ cellspacing="0" cellpadding="4"> [header] - + "https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + @@ -647,7 +647,7 @@ endif::doctype-manpage[] {disable-javascript%

} diff --git a/docs/debugging b/docs/debugging index 7be7c8e5..07bc13a0 100644 --- a/docs/debugging +++ b/docs/debugging @@ -32,8 +32,8 @@ if you can. 4.7-85-g9c15b95 (development version):: Your version is 85 commits newer than 4.7, and the git revision of your -version is +9c15b95+. Go to http://code.i3wm.org/i3/commit/?h=next and see if -the line "commit" starts with the same revision. If so, you are using the +version is +9c15b95+. Go to https://github.com/i3/i3/commits/next and see if +the most recent commit starts with the same revision. If so, you are using the latest version. Development versions of i3 have logging enabled by default and are compiled @@ -109,9 +109,9 @@ No matter whether i3 misbehaved in some way without crashing or whether it just crashed, the logfile provides all information necessary to debug the problem. To upload a compressed version of the logfile (for a bugreport), use: ------------------------------------------------------------------------------- -DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- http://logs.i3wm.org ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- https://logs.i3wm.org +------------------------------------------------------------------------------- This command does not depend on i3 (it also works while i3 displays the crash dialog), but it requires a working X11 connection. @@ -154,9 +154,9 @@ you found the section which clearly highlights the problem, additional information might be necessary to completely diagnose the problem. When debugging with us in IRC, be prepared to use a so called nopaste service -such as http://nopaste.info or http://pastebin.com because pasting large -amounts of text in IRC sometimes leads to incomplete lines (servers have line -length limitations) or flood kicks. +such as https://pastebin.com because pasting large amounts of text in IRC +sometimes leads to incomplete lines (servers have line length limitations) or +flood kicks. == Debugging i3bar diff --git a/docs/hacking-howto b/docs/hacking-howto index d585c2d7..2ca44a5f 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -52,8 +52,8 @@ Here’s a memory refresher: == Using git / sending patches For a short introduction into using git, see -http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy -or, for more documentation, see http://git-scm.com/documentation +https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy +or, for more documentation, see https://git-scm.com/documentation Please talk to us before working on new features to see whether they will be accepted. A good way for this is to open an issue and asking for opinions on it. @@ -156,7 +156,7 @@ workspace, the split container we are talking about is the workspace. To get an impression of how different layouts are represented, just play around and look at the data structures -- they are exposed as a JSON hash. See -http://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an +https://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an example. == Files diff --git a/docs/i3-pod2html b/docs/i3-pod2html index bda7e8d7..bd797fcb 100755 --- a/docs/i3-pod2html +++ b/docs/i3-pod2html @@ -31,7 +31,7 @@ $parser->html_header_before_title( - +