From 8557b05a2c8e5526fcc8e99c96be7fcca172ab7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Sat, 5 May 2012 17:28:42 +0200 Subject: [PATCH 001/200] t/141-resize.t: do not compare float values directly. use i3test.pm::cmp_float which compares using precision of 1e-6. --- testcases/lib/i3test.pm | 9 ++++++++ testcases/t/141-resize.t | 44 ++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index 4c41a7f2..32a17934 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -33,6 +33,7 @@ our @EXPORT = qw( open_floating_window get_dock_clients cmd + cmp_float sync_with_i3 does_i3_live exit_gracefully @@ -563,6 +564,14 @@ sub launch_with_config { return $i3_pid; } +# compares two floats and return true if they differ less +# then 1e-6 +sub cmp_float { + my ($a, $b) = @_; + + return abs($a - $b) < 1e-6; +} + package i3test::X11; use parent 'X11::XCB::Connection'; diff --git a/testcases/t/141-resize.t b/testcases/t/141-resize.t index 91aeca50..86954f13 100644 --- a/testcases/t/141-resize.t +++ b/testcases/t/141-resize.t @@ -22,8 +22,8 @@ cmd 'resize grow up 10 px or 25 ppt'; my ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.25, 'top window got only 25%'); -is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); ############################################################ @@ -34,8 +34,8 @@ cmd 'split h'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.25, 'top window got only 25%'); -is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); ############################################################ # checks that resizing within stacked/tabbed cons works @@ -52,14 +52,14 @@ cmd 'split h'; cmd 'layout stacked'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.5, 'top window got 50%'); -is($nodes->[1]->{percent}, 0.5, 'bottom window got 50%'); +ok(cmp_float($nodes->[0]->{percent}, 0.5), 'top window got 50%'); +ok(cmp_float($nodes->[1]->{percent}, 0.5), 'bottom window got 50%'); cmd 'resize grow up 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.25, 'top window got 25%'); -is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); ############################################################ # Checks that resizing in the parent's parent's orientation works. @@ -79,14 +79,14 @@ $top = open_window; $bottom = open_window; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.5, 'left window got 50%'); -is($nodes->[1]->{percent}, 0.5, 'right window got 50%'); +ok(cmp_float($nodes->[0]->{percent}, 0.5), 'left window got 50%'); +ok(cmp_float($nodes->[1]->{percent}, 0.5), 'right window got 50%'); cmd 'resize grow left 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.25, 'left window got 25%'); -is($nodes->[1]->{percent}, 0.75, 'right window got 75%'); +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%'); ################################################################################ # Check that the resize grow/shrink width/height syntax works. @@ -101,8 +101,8 @@ $right = open_window; cmd 'resize grow width 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.25, 'left window got 25%'); -is($nodes->[1]->{percent}, 0.75, 'right window got 75%'); +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%'); # Now test it with four windows $tmp = fresh_workspace; @@ -112,19 +112,19 @@ open_window for (1..4); cmd 'resize grow width 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); -is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); -is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); -is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); +ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%'); +ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%'); +ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%'); +ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%'); # height should be a no-op in this situation cmd 'resize grow height 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); -is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); -is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); -is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); +ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%'); +ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%'); +ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%'); +ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%'); ############################################################ From d13ba7ca530c6063e3bab626f6d679c999ec933f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Sun, 6 May 2012 11:03:17 +0200 Subject: [PATCH 002/200] Fix floating precision bug When calculating coordinates we should multiply at first otherwise we lose precision when i3 is compiled without sse2 support. The following code prints "Res1: 348 Res2: 349" when compiled with -O0 -mno-sse2 and "Res1: 349 Res2: 349" with -O0 -msee2. Note that -msse2 is default flag on 64bit OSes. int main() { double a = 349.0 / 768; double b = 349.0 * 768; int res1 = a * 768; int res2 = b / 768; printf("Res1: %d Res2: %d\n", res1, res2); return 0; } Thanks guys for helping me to hunt down this one. --- src/floating.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/floating.c b/src/floating.c index 3b691169..c0154aca 100644 --- a/src/floating.c +++ b/src/floating.c @@ -614,12 +614,12 @@ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) { uint32_t rel_y = (con->rect.y - old_rect->y); /* Then we calculate a fraction, for example 0.63 for a window * which is at y = 1212 of a 1920 px high output */ - double fraction_x = ((double)rel_x / old_rect->width); - double fraction_y = ((double)rel_y / old_rect->height); DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n", - rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height); - con->rect.x = new_rect->x + (fraction_x * new_rect->width); - con->rect.y = new_rect->y + (fraction_y * new_rect->height); + rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height, + old_rect->width, old_rect->height); + /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */ + con->rect.x = new_rect->x + (double)(rel_x * new_rect->width) / old_rect->width; + con->rect.y = new_rect->y + (double)(rel_y * new_rect->height) / old_rect->height; DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y); } From da1e2327577ac41e222953428e9a739173e24c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sat, 26 May 2012 17:37:45 -0300 Subject: [PATCH 003/200] Refined the fullscreen focus constraints. Basically, a focus change can't escape a fullscreen container. The only exception is per-output fullscreen containers, as you should be able to focus a container in a different workspace in this case. This is an improvement on 4eab046e, now considering the difference between global and per-output fullscreen and taking the tree structure into account to determine what escaping the fullscreen container means. It only affects targeted focus commands in the form "for_window [...] focus", but it lays the foundation for forthcoming fixes to all other focus commands. --- include/con.h | 23 +++++++ src/commands.c | 10 +-- src/con.c | 56 +++++++++++++++++ testcases/t/156-fullscreen-focus.t | 98 +++++++++++++++++++++++------- 4 files changed, 157 insertions(+), 30 deletions(-) diff --git a/include/con.h b/include/con.h index b14c477e..95726147 100644 --- a/include/con.h +++ b/include/con.h @@ -255,4 +255,27 @@ void con_set_layout(Con *con, int layout); */ Rect con_minimum_size(Con *con); +/** + * Returns true if changing the focus to con would be allowed considering + * the fullscreen focus constraints. Specifically, if a fullscreen container or + * any of its descendants is focused, this function returns true if and only if + * focusing con would mean that focus would still be visible on screen, i.e., + * the newly focused container would not be obscured by a fullscreen container. + * + * In the simplest case, if a fullscreen container or any of its descendants is + * fullscreen, this functions returns true if con is the fullscreen container + * itself or any of its descendants, as this means focus wouldn't escape the + * boundaries of the fullscreen container. + * + * In case the fullscreen container is of type CF_OUTPUT, this function returns + * true if con is on a different workspace, as focus wouldn't be obscured by + * the fullscreen container that is constrained to a different workspace. + * + * Note that this same logic can be applied to moving containers. If a + * container can be focused under the fullscreen focus constraints, it can also + * become a parent or sibling to the currently focused container. + * + */ +bool con_fullscreen_permits_focusing(Con *con); + #endif diff --git a/src/commands.c b/src/commands.c index 1e1ee0ff..b3c955e5 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1262,13 +1262,9 @@ void cmd_focus(I3_CMD) { if (!ws) continue; - /* Don't allow the focus switch if the focused and current - * containers are in the same workspace. */ - if (focused && - focused->type != CT_WORKSPACE && - focused->fullscreen_mode != CF_NONE && - con_get_workspace(focused) == ws) { - LOG("Cannot change focus while in fullscreen mode (same workspace).\n"); + /* Check the fullscreen focus constraints. */ + if (!con_fullscreen_permits_focusing(current->con)) { + LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n"); ysuccess(false); return; } diff --git a/src/con.c b/src/con.c index c24a379f..f969d056 100644 --- a/src/con.c +++ b/src/con.c @@ -1132,3 +1132,59 @@ Rect con_minimum_size(Con *con) { con->type, con->layout, con->orientation); assert(false); } + +/* + * Returns true if changing the focus to con would be allowed considering + * the fullscreen focus constraints. Specifically, if a fullscreen container or + * any of its descendants is focused, this function returns true if and only if + * focusing con would mean that focus would still be visible on screen, i.e., + * the newly focused container would not be obscured by a fullscreen container. + * + * In the simplest case, if a fullscreen container or any of its descendants is + * fullscreen, this functions returns true if con is the fullscreen container + * itself or any of its descendants, as this means focus wouldn't escape the + * boundaries of the fullscreen container. + * + * In case the fullscreen container is of type CF_OUTPUT, this function returns + * true if con is on a different workspace, as focus wouldn't be obscured by + * the fullscreen container that is constrained to a different workspace. + * + * Note that this same logic can be applied to moving containers. If a + * container can be focused under the fullscreen focus constraints, it can also + * become a parent or sibling to the currently focused container. + * + */ +bool con_fullscreen_permits_focusing(Con *con) { + /* No focus, no problem. */ + if (!focused) + return true; + + /* Find the first fullscreen ascendent. */ + Con *fs = focused; + while (fs && fs->fullscreen_mode == CF_NONE) + fs = fs->parent; + + /* The most common case is we hit the workspace level. In this + * situation, changing focus is also harmless. */ + assert(fs->fullscreen_mode != CF_NONE); + if (fs->type == CT_WORKSPACE) + return true; + + /* If fullscreen is per-output, the focus being in a different workspace is + * sufficient to guarantee that change won't leave fullscreen in bad shape. */ + if (fs->fullscreen_mode == CF_OUTPUT && + con_get_workspace(con) != con_get_workspace(fs)) { + return true; + } + + /* Allow it only if the container to be focused is contained within the + * current fullscreen container. */ + do { + if (con->parent == fs) + return true; + con = con->parent; + } while (con); + + /* Focusing con would hide it behind a fullscreen window, disallow it. */ + return false; +} diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index f9dc6dce..3a27c9ff 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -11,9 +11,9 @@ my $i3 = i3(get_socket_path()); my $tmp = fresh_workspace; -##################################################################### -# open the left window -##################################################################### +################################################################################ +# Open the left window. +################################################################################ my $left = open_window({ background_color => '#ff0000' }); @@ -21,23 +21,26 @@ is($x->input_focus, $left->id, 'left window focused'); diag("left = " . $left->id); -##################################################################### -# Open the right window -##################################################################### +################################################################################ +# Open the right window. +################################################################################ my $right = open_window({ background_color => '#00ff00' }); diag("right = " . $right->id); -##################################################################### -# Set the right window to fullscreen -##################################################################### +################################################################################ +# Set the right window to fullscreen. +################################################################################ + cmd 'nop setting fullscreen'; cmd 'fullscreen'; -##################################################################### -# Open a third window -##################################################################### +################################################################################ +# Open a third window. Since we're fullscreen, the window won't be # mapped, so +# don't wait for it to be mapped. Instead, just send the map request and sync +# with i3 to make sure i3 recognizes it. +################################################################################ my $third = open_window({ background_color => '#0000ff', @@ -51,13 +54,15 @@ sync_with_i3; diag("third = " . $third->id); -# move the fullscreen window to a different ws +################################################################################ +# Move the window to a different workspace, and verify that the third window now +# gets focused in the current workspace. +################################################################################ my $tmp2 = get_unused_workspace; cmd "move workspace $tmp2"; -# verify that the third window has the focus is($x->input_focus, $third->id, 'third window focused'); ################################################################################ @@ -87,20 +92,67 @@ is($nodes->[0]->{id}, $old_id, 'id unchanged'); is($nodes->[0]->{focused}, 1, 'fullscreen window focused'); ################################################################################ -# Make sure it's possible to focus a container in a different workspace even if -# we are currently focusing a fullscreen container. +# Ensure it's possible to change focus if it doesn't escape the fullscreen +# container with fullscreen global. We can't even focus a container in a +# different workspace. ################################################################################ -$tmp2 = fresh_workspace; -my $focusable_window = open_window; +cmd 'fullscreen'; +$tmp = fresh_workspace; cmd "workspace $tmp"; -cmd '[id="' . $focusable_window->id . '"] focus'; +my $diff_ws = open_window; -is(focused_ws(), $tmp2, 'focus went to a different workspace'); +$tmp2 = fresh_workspace; +cmd "workspace $tmp2"; +cmd 'split h'; -$nodes = get_ws_content($tmp2); -is(scalar @$nodes, 1, 'precisely one window'); -is($nodes->[0]->{focused}, 1, 'focusable window focused'); +$left = open_window; +my $right1 = open_window; +cmd 'split v'; +my $right2 = open_window; +$nodes = get_ws_content($tmp); + +cmd 'focus parent'; +cmd 'fullscreen global'; + +cmd '[id="' . $right1->id . '"] focus'; +is($x->input_focus, $right1->id, 'upper right window focused'); + +cmd '[id="' . $right2->id . '"] focus'; +is($x->input_focus, $right2->id, 'bottom right window focused'); + +cmd '[id="' . $left->id . '"] focus'; +is($x->input_focus, $right2->id, 'prevented focus change to left window'); + +cmd '[id="' . $diff_ws->id . '"] focus'; +is($x->input_focus, $right2->id, 'prevented focus change to different ws'); + +################################################################################ +# Same tests when we're in non-global fullscreen mode. We toggle fullscreen on +# and off to avoid testing whether focus level works in fullscreen for now. It +# should now be possible to focus a container in a different workspace. +################################################################################ + +cmd 'fullscreen global'; +cmd 'fullscreen global'; + +cmd '[id="' . $right1->id . '"] focus'; +is($x->input_focus, $right1->id, 'upper right window focused'); + +cmd 'focus parent'; +cmd 'fullscreen'; + +cmd '[id="' . $right1->id . '"] focus'; +is($x->input_focus, $right1->id, 'upper right window still focused'); + +cmd '[id="' . $right2->id . '"] focus'; +is($x->input_focus, $right2->id, 'bottom right window focused'); + +cmd '[id="' . $left->id . '"] focus'; +is($x->input_focus, $right2->id, 'prevented focus change to left window'); + +cmd '[id="' . $diff_ws->id . '"] focus'; +is($x->input_focus, $diff_ws->id, 'allowed focus change to different ws'); done_testing; From 250c260eaadc77ec4b0bd73107e8f863a55f8dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sat, 26 May 2012 18:36:25 -0300 Subject: [PATCH 004/200] Allow focus child/parent when in fullscreen. This is now restricted according to the already defined fullscreen focus constraints. Test case 157 was removed, as we don't prevent level up/down in fullscreen anymore. Those commands are properly tested in fullscreen by test case 156. Fixes: #612 --- include/tree.h | 8 ++-- src/commands.c | 29 +++++++------ src/con.c | 4 ++ src/tree.c | 20 ++++----- testcases/t/156-fullscreen-focus.t | 26 +++++++----- testcases/t/157-regress-fullscreen-level-up.t | 41 ------------------- 6 files changed, 48 insertions(+), 80 deletions(-) delete mode 100644 testcases/t/157-regress-fullscreen-level-up.t diff --git a/include/tree.h b/include/tree.h index b9159e3b..8816b19a 100644 --- a/include/tree.h +++ b/include/tree.h @@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window); void tree_split(Con *con, orientation_t orientation); /** - * Moves focus one level up. + * Moves focus one level up. Returns true if focus changed. * */ -void level_up(void); +bool level_up(void); /** - * Moves focus one level down. + * Moves focus one level down. Returns true if focus changed. * */ -void level_down(void); +bool level_down(void); /** * Renders the tree, that is rendering all outputs using render_con() and diff --git a/src/commands.c b/src/commands.c index b3c955e5..cf8187b7 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1213,23 +1213,26 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) { * */ void cmd_focus_level(I3_CMD, char *level) { - if (focused && - focused->type != CT_WORKSPACE && - focused->fullscreen_mode != CF_NONE) { - LOG("Cannot change focus while in fullscreen mode.\n"); - ysuccess(false); - return; + DLOG("level = %s\n", level); + bool success = false; + + /* Focusing the parent can only be allowed if the newly + * focused container won't escape the fullscreen container. */ + if (strcmp(level, "parent") == 0) { + if (focused && focused->parent) { + if (con_fullscreen_permits_focusing(focused->parent)) + success = level_up(); + else + LOG("Currently in fullscreen, not going up\n"); + } } - DLOG("level = %s\n", level); + /* Focusing a child should always be allowed. */ + else success = level_down(); - if (strcmp(level, "parent") == 0) - level_up(); - else level_down(); - - cmd_output->needs_tree_render = true; + cmd_output->needs_tree_render = success; // XXX: default reply for now, make this a better reply - ysuccess(true); + ysuccess(success); } /* diff --git a/src/con.c b/src/con.c index f969d056..a7ae642b 100644 --- a/src/con.c +++ b/src/con.c @@ -1170,6 +1170,10 @@ bool con_fullscreen_permits_focusing(Con *con) { if (fs->type == CT_WORKSPACE) return true; + /* Allow it if the container itself is the fullscreen container. */ + if (con == fs) + return true; + /* If fullscreen is per-output, the focus being in a different workspace is * sufficient to guarantee that change won't leave fullscreen in bad shape. */ if (fs->fullscreen_mode == CF_OUTPUT && diff --git a/src/tree.c b/src/tree.c index f29369c6..cb3d044a 100644 --- a/src/tree.c +++ b/src/tree.c @@ -375,38 +375,34 @@ void tree_split(Con *con, orientation_t orientation) { } /* - * Moves focus one level up. + * Moves focus one level up. Returns true if focus changed. * */ -void level_up(void) { - /* We cannot go up when we are in fullscreen mode at the moment, that would - * be totally not intuitive */ - if (focused->fullscreen_mode != CF_NONE) { - LOG("Currently in fullscreen, not going up\n"); - return; - } +bool level_up(void) { /* We can focus up to the workspace, but not any higher in the tree */ if ((focused->parent->type != CT_CON && focused->parent->type != CT_WORKSPACE) || focused->type == CT_WORKSPACE) { LOG("Cannot go up any further\n"); - return; + return false; } con_focus(focused->parent); + return true; } /* - * Moves focus one level down. + * Moves focus one level down. Returns true if focus changed. * */ -void level_down(void) { +bool level_down(void) { /* Go down the focus stack of the current node */ Con *next = TAILQ_FIRST(&(focused->focus_head)); if (next == TAILQ_END(&(focused->focus_head))) { printf("cannot go down\n"); - return; + return false; } con_focus(next); + return true; } static void mark_unmapped(Con *con) { diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index 3a27c9ff..65d23815 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -122,6 +122,12 @@ is($x->input_focus, $right1->id, 'upper right window focused'); cmd '[id="' . $right2->id . '"] focus'; is($x->input_focus, $right2->id, 'bottom right window focused'); +cmd 'focus parent'; +isnt($x->input_focus, $right2->id, 'bottom right window no longer focused'); + +cmd 'focus child'; +is($x->input_focus, $right2->id, 'bottom right window focused again'); + cmd '[id="' . $left->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to left window'); @@ -129,26 +135,26 @@ cmd '[id="' . $diff_ws->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to different ws'); ################################################################################ -# Same tests when we're in non-global fullscreen mode. We toggle fullscreen on -# and off to avoid testing whether focus level works in fullscreen for now. It -# should now be possible to focus a container in a different workspace. +# Same tests when we're in non-global fullscreen mode. It should now be possible +# to focus a container in a different workspace. ################################################################################ +cmd 'focus parent'; cmd 'fullscreen global'; -cmd 'fullscreen global'; +cmd 'fullscreen'; cmd '[id="' . $right1->id . '"] focus'; is($x->input_focus, $right1->id, 'upper right window focused'); -cmd 'focus parent'; -cmd 'fullscreen'; - -cmd '[id="' . $right1->id . '"] focus'; -is($x->input_focus, $right1->id, 'upper right window still focused'); - cmd '[id="' . $right2->id . '"] focus'; is($x->input_focus, $right2->id, 'bottom right window focused'); +cmd 'focus parent'; +isnt($x->input_focus, $right2->id, 'bottom right window no longer focused'); + +cmd 'focus child'; +is($x->input_focus, $right2->id, 'bottom right window focused again'); + cmd '[id="' . $left->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to left window'); diff --git a/testcases/t/157-regress-fullscreen-level-up.t b/testcases/t/157-regress-fullscreen-level-up.t deleted file mode 100644 index 316dbcaa..00000000 --- a/testcases/t/157-regress-fullscreen-level-up.t +++ /dev/null @@ -1,41 +0,0 @@ -#!perl -# vim:ts=4:sw=4:expandtab -# -# Regression test: level up should be a noop during fullscreen mode -# -use i3test; - -my $tmp = fresh_workspace; - -##################################################################### -# open a window, verify it’s not in fullscreen mode -##################################################################### - -my $win = open_window; - -my $nodes = get_ws_content $tmp; -is(@$nodes, 1, 'exactly one client'); -is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen'); - -##################################################################### -# make it fullscreen -##################################################################### - -cmd 'nop making fullscreen'; -cmd 'fullscreen'; - -$nodes = get_ws_content $tmp; -is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now'); - -##################################################################### -# send level up, try to un-fullscreen -##################################################################### -cmd 'focus parent'; -cmd 'fullscreen'; - -$nodes = get_ws_content $tmp; -is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen any longer'); - -does_i3_live; - -done_testing; From 8bf4e9e05965a1bbcada062026a214c4356c4162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sat, 26 May 2012 19:53:02 -0300 Subject: [PATCH 005/200] Restrict directional focus in fullscreen. This reuses the same fullscreen focus logic to ensure that focus doesn't escape a fullscreen container. --- src/tree.c | 4 ++ testcases/t/156-fullscreen-focus.t | 73 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/tree.c b/src/tree.c index cb3d044a..3ed392ac 100644 --- a/src/tree.c +++ b/src/tree.c @@ -556,6 +556,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) else next = TAILQ_LAST(&(parent->nodes_head), nodes_head); } + /* Don't violate fullscreen focus restrictions. */ + if (!con_fullscreen_permits_focusing(next)) + return false; + /* 3: focus choice comes in here. at the moment we will go down * until we find a window */ /* TODO: check for window, atm we only go down as far as possible */ diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index 65d23815..f76588f9 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -131,6 +131,24 @@ is($x->input_focus, $right2->id, 'bottom right window focused again'); cmd '[id="' . $left->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to left window'); +cmd 'focus up'; +is($x->input_focus, $right1->id, 'allowed focus up'); + +cmd 'focus down'; +is($x->input_focus, $right2->id, 'allowed focus down'); + +cmd 'focus left'; +is($x->input_focus, $right2->id, 'prevented focus left'); + +cmd 'focus right'; +is($x->input_focus, $right2->id, 'prevented focus right'); + +cmd 'focus down'; +is($x->input_focus, $right1->id, 'allowed focus wrap (down)'); + +cmd 'focus up'; +is($x->input_focus, $right2->id, 'allowed focus wrap (up)'); + cmd '[id="' . $diff_ws->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to different ws'); @@ -158,7 +176,62 @@ is($x->input_focus, $right2->id, 'bottom right window focused again'); cmd '[id="' . $left->id . '"] focus'; is($x->input_focus, $right2->id, 'prevented focus change to left window'); +cmd 'focus up'; +is($x->input_focus, $right1->id, 'allowed focus up'); + +cmd 'focus down'; +is($x->input_focus, $right2->id, 'allowed focus down'); + +cmd 'focus left'; +is($x->input_focus, $right2->id, 'prevented focus left'); + +cmd 'focus right'; +is($x->input_focus, $right2->id, 'prevented focus right'); + +cmd 'focus down'; +is($x->input_focus, $right1->id, 'allowed focus wrap (down)'); + +cmd 'focus up'; +is($x->input_focus, $right2->id, 'allowed focus wrap (up)'); + cmd '[id="' . $diff_ws->id . '"] focus'; is($x->input_focus, $diff_ws->id, 'allowed focus change to different ws'); +################################################################################ +# More testing of the interaction between wrapping and the fullscreen focus +# restrictions. +################################################################################ + +cmd '[id="' . $right1->id . '"] focus'; +is($x->input_focus, $right1->id, 'upper right window focused'); + +cmd 'focus parent'; +cmd 'fullscreen'; +cmd 'focus child'; + +cmd 'split v'; +my $right12 = open_window; + +cmd 'focus down'; +is($x->input_focus, $right2->id, 'bottom right window focused'); + +cmd 'split v'; +my $right22 = open_window; + +cmd 'focus parent'; +cmd 'fullscreen'; +cmd 'focus child'; + +cmd 'focus down'; +is($x->input_focus, $right2->id, 'focus did not leave parent container (1)'); + +cmd 'focus down'; +is($x->input_focus, $right22->id, 'focus did not leave parent container (2)'); + +cmd 'focus up'; +is($x->input_focus, $right2->id, 'focus did not leave parent container (3)'); + +cmd 'focus up'; +is($x->input_focus, $right22->id, 'focus did not leave parent container (4)'); + done_testing; From 4452bfb942d06d76e5fc317b50b99872ec086059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sat, 26 May 2012 21:45:15 -0300 Subject: [PATCH 006/200] Prevent moving out of fullscreen containers. --- src/move.c | 6 ++++++ testcases/t/156-fullscreen-focus.t | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/move.c b/src/move.c index d3065c24..d110312a 100644 --- a/src/move.c +++ b/src/move.c @@ -169,6 +169,12 @@ void tree_move(int direction) { while (above->parent != same_orientation) above = above->parent; + /* Enforce the fullscreen focus restrictions. */ + if (!con_fullscreen_permits_focusing(above->parent)) { + LOG("Cannot move out of fullscreen container\n"); + return; + } + DLOG("above = %p\n", above); Con *next; position_t position; diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index f76588f9..af205c45 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -111,7 +111,6 @@ $left = open_window; my $right1 = open_window; cmd 'split v'; my $right2 = open_window; -$nodes = get_ws_content($tmp); cmd 'focus parent'; cmd 'fullscreen global'; @@ -234,4 +233,26 @@ is($x->input_focus, $right2->id, 'focus did not leave parent container (3)'); cmd 'focus up'; is($x->input_focus, $right22->id, 'focus did not leave parent container (4)'); +################################################################################ +# Ensure that moving in a direction doesn't violate the focus restrictions. +################################################################################ + +sub verify_move_prevented { + my $msg = shift; + my $nodes = get_ws_content($tmp2); + my $split = $nodes->[1]; + my $fs = $split->{nodes}->[1]; + is(scalar @{$fs->{nodes}}, 2, $msg); +} + +cmd 'move left'; +verify_move_prevented('prevented move left'); +cmd 'move right'; +verify_move_prevented('prevented move right'); +cmd 'move down'; +verify_move_prevented('prevented move down'); +cmd 'move up'; +cmd 'move up'; +verify_move_prevented('prevented move up'); + done_testing; From fffc53c24655ef626b18763bae8c3e5950400f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sat, 26 May 2012 22:13:16 -0300 Subject: [PATCH 007/200] Restrict "move to workspace" commands in fullscreen. --- src/con.c | 6 ++++ testcases/t/156-fullscreen-focus.t | 49 ++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/con.c b/src/con.c index a7ae642b..a1491e03 100644 --- a/src/con.c +++ b/src/con.c @@ -576,6 +576,12 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool return; } + /* Prevent moving if this would violate the fullscreen focus restrictions. */ + if (!con_fullscreen_permits_focusing(workspace)) { + LOG("Cannot move out of a fullscreen container"); + return; + } + if (con_is_floating(con)) { DLOG("Using FLOATINGCON instead\n"); con = con->parent; diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index af205c45..b779ce7d 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -237,22 +237,59 @@ is($x->input_focus, $right22->id, 'focus did not leave parent container (4)'); # Ensure that moving in a direction doesn't violate the focus restrictions. ################################################################################ -sub verify_move_prevented { +sub verify_move { + my $num = shift; my $msg = shift; my $nodes = get_ws_content($tmp2); my $split = $nodes->[1]; my $fs = $split->{nodes}->[1]; - is(scalar @{$fs->{nodes}}, 2, $msg); + is(scalar @{$fs->{nodes}}, $num, $msg); } cmd 'move left'; -verify_move_prevented('prevented move left'); +verify_move(2, 'prevented move left'); cmd 'move right'; -verify_move_prevented('prevented move right'); +verify_move(2, 'prevented move right'); cmd 'move down'; -verify_move_prevented('prevented move down'); +verify_move(2, 'prevented move down'); cmd 'move up'; cmd 'move up'; -verify_move_prevented('prevented move up'); +verify_move(2, 'prevented move up'); + +################################################################################ +# Moving to a different workspace is allowed with per-output fullscreen +# containers. +################################################################################ + +cmd "move to workspace $tmp"; +verify_move(1, 'did not prevent move to workspace by name'); + +cmd "workspace $tmp"; +cmd "move to workspace $tmp2"; +cmd "workspace $tmp2"; + +cmd "move to workspace prev"; +verify_move(1, 'did not prevent move to workspace by position'); + +################################################################################ +# Ensure that is not allowed with global fullscreen containers. +################################################################################ + +cmd "workspace $tmp"; +cmd "move to workspace $tmp2"; +cmd "workspace $tmp2"; + +cmd 'focus parent'; +cmd 'fullscreen'; +cmd 'fullscreen global'; +cmd 'focus child'; + +cmd "move to workspace $tmp"; +verify_move(2, 'prevented move to workspace by name'); + +cmd "move to workspace prev"; +verify_move(2, 'prevented move to workspace by position'); + +# TODO: Tests for "move to output" and "move workspace to output". done_testing; From f2dde5f5dd48c8310e4bf380def58614fa1f2a16 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 3 Jun 2012 17:17:11 +0200 Subject: [PATCH 008/200] update refcard (Thanks Moritz Bandemer) --- docs/refcard.html | 134 +++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/docs/refcard.html b/docs/refcard.html index a4427f4f..7156da36 100644 --- a/docs/refcard.html +++ b/docs/refcard.html @@ -42,76 +42,99 @@

+

Basics

- - - - - +
+ + + open new terminal -
+j + + j focus left
+k + + k focus down
+l + + l focus up
+; + + ; focus right +
+ + toggle focus mode
-
-

Changing the container layout

+

Moving windows

- - - +
+e - default - + + + j + move window left
+s - stacking - + + + k + move window down
+w - tabbed + + + l + move window up +
+ + ; + move window right
-

Fullscreen mode

+

Modifying windows

- + + +
+f + + f toggle fullscreen +
+ v + split a window vertically +
+ h + split a window horizontally +
+ r + resize mode +
+

Look at the “Resizing containers / windows” section of the user guide.

+
+ +
+

Changing the container layout

+ + + + +
+ e + default + +
+ s + stacking + +
+ w + tabbed
-
-

Opening other applications

+

Floating

-
+d - open application (with dmenu) -
- - -
-

Closing windows

- + -
+ + + toggle floating
++ q - kill a window + + + drag floating
@@ -120,17 +143,10 @@

Using workspaces

-
+19 + + 0-9 switch to another workspace -
-
- - -
-

Moving windows to workspaces

- -
+ + 19 + + + 0-9 move a window to another workspace
@@ -138,37 +154,33 @@
-

Resizing

-

Look at “Resizing containers / windows” section of the user guide.

+

Opening applications / Closing windows

+ + + +
+ d + open application launcher (dmenu) +
+ + q + kill a window +
-

Restart / Exit

- + -
+ + r + + + c + reload the configuration file +
+ + r restart i3 inplace
+ + e - - exit i3 -
- - -
-

Floating

- - - -
+ + - toggle floating - -
+ - drag floating -
+ + + e + exit i3
+ +

Permission is granted to copy, distribute and/or modify this document provided From 4611f875ffe50b05af7b347bb4600c58f8d7acaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Wed, 9 May 2012 23:39:44 +0200 Subject: [PATCH 009/200] Fix a mistake in comment --- src/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.c b/src/commands.c index cf8187b7..c3f7e4c4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -23,7 +23,7 @@ } while (0) /** When the command did not include match criteria (!), we use the currently - * focused command. Do not confuse this case with a command which included + * focused container. Do not confuse this case with a command which included * criteria but which did not match any windows. This macro has to be called in * every command. */ From 51173baf28a7dcec2a75f1410e95ac9569c48f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Wed, 9 May 2012 23:43:21 +0200 Subject: [PATCH 010/200] Fix 'move to workspace' when used with criteria When moving window from other (not current) workspace to another workspace with criteria we should stay on current workspace. And we should exit early when criteria was specified but didn't match any window. --- src/commands.c | 25 +++++++++++++++++++------ src/con.c | 18 ++++++++++++++++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/commands.c b/src/commands.c index c3f7e4c4..17d700c4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -359,6 +359,15 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { DLOG("which=%s\n", which); + /* We have nothing to move: + * when criteria was specified but didn't match any window or + * when criteria wasn't specified and we don't have any window focused. */ + if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || + (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { + ysuccess(false); + return; + } + HANDLE_EMPTY_MATCH; /* get the workspace */ @@ -400,9 +409,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { owindow *current; - /* Error out early to not create a non-existing workspace (in - * workspace_get()) if we are not actually able to move anything. */ - if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) { + /* We have nothing to move: + * when criteria was specified but didn't match any window or + * when criteria wasn't specified and we don't have any window focused. */ + if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || + (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { ysuccess(false); return; } @@ -430,9 +441,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { void cmd_move_con_to_workspace_number(I3_CMD, char *which) { owindow *current; - /* Error out early to not create a non-existing workspace (in - * workspace_get()) if we are not actually able to move anything. */ - if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) { + /* We have nothing to move: + * when criteria was specified but didn't match any window or + * when criteria wasn't specified and we don't have any window focused. */ + if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || + (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { ysuccess(false); return; } diff --git a/src/con.c b/src/con.c index a1491e03..f804a204 100644 --- a/src/con.c +++ b/src/con.c @@ -587,6 +587,16 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool con = con->parent; } + Con *source_ws = con_get_workspace(con); + if (workspace == source_ws) { + DLOG("Not moving, already there\n"); + return; + } + + /* Save the current workspace. So we can call workspace_show() by the end + * of this function. */ + Con *current_ws = con_get_workspace(focused); + Con *source_output = con_get_output(con), *dest_output = con_get_output(workspace); @@ -669,8 +679,12 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool /* Descend focus stack in case focus_next is a workspace which can * occur if we move to the same workspace. Also show current workspace * to ensure it is focused. */ - workspace_show(con_get_workspace(focus_next)); - con_focus(con_descend_focused(focus_next)); + workspace_show(current_ws); + + /* Set focus only if con was on current workspace before moving. + * Otherwise we would give focus to some window on different workspace. */ + if (source_ws == current_ws) + con_focus(con_descend_focused(focus_next)); } CALL(parent, on_remove_child); From 2afecaf35532c19f5d51440942c66572b8ab5653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20L=C3=B6bl?= Date: Wed, 9 May 2012 23:45:12 +0200 Subject: [PATCH 011/200] Add a new command 'move to workspace current' Added a new command 'move to workspace current' which can be used with criteria to move a window to the current workspace. --- docs/userguide | 20 ++++++++++++++++++-- parser-specs/commands.spec | 4 ++-- src/commands.c | 4 +++- testcases/t/132-move-workspace.t | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/docs/userguide b/docs/userguide index 41452793..37ddf4f8 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1293,8 +1293,9 @@ You can also switch to the next and previous workspace with the commands workspace 1, 3, 4 and 9 and you want to cycle through them with a single key combination. To restrict those to the current output, use +workspace next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move -container to workspace next+ and +move container to workspace prev+ to move a -container to the next/previous workspace. +container to workspace next+, +move container to workspace prev+ to move a +container to the next/previous workspace and +move container to workspace current+ +(the last one makes sense only when used with criteria). [[back_and_forth]] To switch back to the previously focused workspace, use +workspace @@ -1310,6 +1311,18 @@ you can use the +move workspace to output+ command followed by the name of the target output. You may also use +left+, +right+, +up+, +down+ instead of the xrandr output name to move to the next output in the specified direction. +*Syntax*: +----------------------------------- +workspace +workspace back_and_forth +workspace +workspace number + +move [window|container] [to] workspace +move [window|container] [to] workspace number +move [window|container] [to] workspace +----------------------------------- + *Examples*: ------------------------- bindsym mod+1 workspace 1 @@ -1325,6 +1338,9 @@ bindsym mod+b workspace back_and_forth # move the whole workspace to the next output bindsym mod+x move workspace to output right + +# move firefox to current workspace +bindsym mod+F1 [class="Firefox"] move workspace current ------------------------- ==== Named workspaces diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index 684fd23e..100e5f4e 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -190,7 +190,7 @@ state RENAME_WORKSPACE_TO: -> call cmd_rename_workspace($old_name, $new_name) # move [ [px]] -# move [window|container] [to] workspace +# move [window|container] [to] workspace [|next|prev|current] # move [window|container] [to] output # move [window|container] [to] scratchpad # move workspace to [output] @@ -231,7 +231,7 @@ state MOVE_DIRECTION_PX: state MOVE_WORKSPACE: 'to' -> MOVE_WORKSPACE_TO_OUTPUT - workspace = 'next', 'prev', 'next_on_output', 'prev_on_output' + workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current' -> call cmd_move_con_to_workspace($workspace) 'number' -> MOVE_WORKSPACE_NUMBER diff --git a/src/commands.c b/src/commands.c index 17d700c4..6f0d0c5e 100644 --- a/src/commands.c +++ b/src/commands.c @@ -351,7 +351,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { /* * Implementation of 'move [window|container] [to] workspace - * next|prev|next_on_output|prev_on_output'. + * next|prev|next_on_output|prev_on_output|current'. * */ void cmd_move_con_to_workspace(I3_CMD, char *which) { @@ -380,6 +380,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { ws = workspace_next_on_output(); else if (strcmp(which, "prev_on_output") == 0) ws = workspace_prev_on_output(); + else if (strcmp(which, "current") == 0) + ws = con_get_workspace(focused); else { ELOG("BUG: called with which=%s\n", which); ysuccess(false); diff --git a/testcases/t/132-move-workspace.t b/testcases/t/132-move-workspace.t index 3f00428c..79753cd7 100644 --- a/testcases/t/132-move-workspace.t +++ b/testcases/t/132-move-workspace.t @@ -99,6 +99,26 @@ cmd 'move workspace prev'; ok(@{get_ws_content($tmp)} == 3, 'three containers on first ws'); ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws'); +################################################################### +# check if 'move workspace current' works +################################################################### + +$tmp = get_unused_workspace(); +$tmp2 = get_unused_workspace(); + +cmd "workspace $tmp"; +$first = open_window(name => 'win-name'); +ok(@{get_ws_content($tmp)} == 1, 'one container on first ws'); + +cmd "workspace $tmp2"; +ok(@{get_ws_content($tmp2)} == 0, 'no containers yet'); + +cmd qq|[title="win-name"] move workspace $tmp2|; +ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws'); + +cmd qq|[title="win-name"] move workspace $tmp|; +ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws'); + ################################################################### # check if floating cons are moved to new workspaces properly # (that is, if they are floating on the target ws, too) From ecd238b65eba9754f0e017b9bc953af5a6069799 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 3 Jun 2012 19:44:42 +0200 Subject: [PATCH 012/200] ignore "current" when searching for named workspaces --- src/workspace.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/workspace.c b/src/workspace.c index 928f0bd6..3d08fa4c 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -114,14 +114,15 @@ Con *create_workspace_on_output(Output *output, Con *content) { /* We check if this is the workspace * next/prev/next_on_output/prev_on_output/back_and_forth/number command. * Beware: The workspace names "next", "prev", "next_on_output", - * "prev_on_output", "number" and "back_and_forth" are OK, so we check - * before stripping the double quotes */ + * "prev_on_output", "number", "back_and_forth" and "current" are OK, + * so we check before stripping the double quotes */ if (strncasecmp(target, "next", strlen("next")) == 0 || strncasecmp(target, "prev", strlen("prev")) == 0 || strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 || strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 || strncasecmp(target, "number", strlen("number")) == 0 || - strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0) + strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 || + strncasecmp(target, "current", strlen("current")) == 0) continue; if (*target == '"') target++; From 19946ee14d2e4262165f05c717c59143e581db19 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 29 Jun 2012 21:21:07 +0200 Subject: [PATCH 013/200] tests: make t/159-socketpath more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …by getting the socket path from i3 and then checking that it conforms to what we expect. Previously we monitored /tmp, which can go wrong in various ways, especially since i3’s directory within /tmp is not predictable (by design). --- testcases/t/159-socketpaths.t | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/testcases/t/159-socketpaths.t b/testcases/t/159-socketpaths.t index c63bbbc4..7c3946b4 100644 --- a/testcases/t/159-socketpaths.t +++ b/testcases/t/159-socketpaths.t @@ -5,6 +5,7 @@ # use i3test i3_autostart => 0; use File::Temp qw(tempfile tempdir); +use File::Basename; use POSIX qw(getuid); use v5.10; @@ -20,21 +21,14 @@ EOT # ensure XDG_RUNTIME_DIR is not set delete $ENV{XDG_RUNTIME_DIR}; -# See which files exist in /tmp before to not mistakenly check an already -# existing tmpdir of another i3 instance. -my @files_before = ; my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1); -my @files_after = ; -@files_after = grep { !($_ ~~ @files_before) } @files_after; - -is(@files_after, 1, 'one new temp directory'); - +my $socketpath = get_socket_path(0); my $folder = "/tmp/i3-" . getpwuid(getuid()); -like($files_after[0], qr/^$folder/, 'temp directory matches expected pattern'); -$folder = $files_after[0]; +like(dirname($socketpath), qr/^$folder/, 'temp directory matches expected pattern'); +$folder = dirname($socketpath); ok(-d $folder, "folder $folder exists"); -my $socketpath = "$folder/ipc-socket." . $pid; +$socketpath = "$folder/ipc-socket." . $pid; ok(-S $socketpath, "file $socketpath exists and is a socket"); exit_gracefully($pid); From daee3218c9aec3499bf128ba1649099d4f93bcd9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 29 Jun 2012 21:28:10 +0200 Subject: [PATCH 014/200] tests: make t/166-assign.t more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …by using the new syntax which will not trigger i3-nagbar. Checking for i3-nagbar is inherently prone to race conditions since i3-nagbar does not communicate in any way that it’s there. --- testcases/t/166-assign.t | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index d79c1000..a4a70c8b 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -206,24 +206,16 @@ sub i3nagbar_running { $config = < $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'), @@ -233,7 +225,7 @@ $content = get_ws($tmp); ok(@{$content->{nodes}} == 0, 'no tiling cons'); ok(@{$content->{floating_nodes}} == 0, 'one floating con'); @docked = get_dock_clients; -is(@docked, 2, 'two dock clients now'); +is(@docked, 1, 'one dock client now'); $window->destroy; From c5d047215865fad385f4f91798e3e3bd3959ebea Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 29 Jun 2012 23:13:25 +0200 Subject: [PATCH 015/200] Explicitly disconnect in the 'exit' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a race condition when running the tests. I think that the X11 server has more time to clean up the resources when we do an explicit disconnect. The symptom I was seeing was that sometimes, i3 couldn’t become the window manager on one of the Xdummy instances. --- src/commands.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands.c b/src/commands.c index 6f0d0c5e..f30d6612 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1410,6 +1410,7 @@ void cmd_layout(I3_CMD, char *layout_str) { */ void cmd_exit(I3_CMD) { LOG("Exiting due to user command.\n"); + xcb_disconnect(conn); exit(0); /* unreached */ From f0f45aa84d5eee0002ee4eca2646ec92fae56746 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 29 Jun 2012 23:20:46 +0200 Subject: [PATCH 016/200] tests: t/166-assign.t: use wait_for_window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While it’s generally intended that wait_for_window is not called within this testcase, in the first test instruction it was a mistake. The window in fact gets mapped and therefore we should call wait_for_window. --- testcases/t/166-assign.t | 1 + 1 file changed, 1 insertion(+) diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index a4a70c8b..3a8a10d5 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -57,6 +57,7 @@ my $tmp = fresh_workspace; ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); my $window = open_special; +wait_for_map($window); ok(@{get_ws_content($tmp)} == 1, 'special window got managed to current (random) workspace'); From a65394646cf24d679bd5c3d546db72e7cde6a65b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 29 Jun 2012 23:21:32 +0200 Subject: [PATCH 017/200] tests: t/504: ensure cursor position MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, the cursor might be at (1025, 0) due to previous testcases and we wouldn’t start up on the correct workspace. --- testcases/t/504-move-workspace-to-output.t | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testcases/t/504-move-workspace-to-output.t b/testcases/t/504-move-workspace-to-output.t index 57e56943..8357e0da 100644 --- a/testcases/t/504-move-workspace-to-output.t +++ b/testcases/t/504-move-workspace-to-output.t @@ -9,6 +9,10 @@ use i3test i3_autostart => 0; # TODO: # introduce 'move workspace 3 to output ' with synonym 'move workspace 3 to ' +# 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 = < Date: Sun, 22 Jul 2012 00:16:52 +0200 Subject: [PATCH 018/200] i3: Replace loglevels by a global debug logging File-limited were not used nor really useful Besides, they are painful to maintain in Makefile rules compared to the benefit --- .gitignore | 2 -- Makefile | 36 +++++++----------------------------- docs/hacking-howto | 2 +- include/log.h | 13 ++++++------- include/regex.h | 2 +- man/i3-dump-log.man | 2 +- man/i3.man | 7 ++++--- src/commands_parser.c | 4 ++-- src/log.c | 36 +++++++++--------------------------- src/main.c | 8 ++++---- src/regex.c | 2 +- 11 files changed, 36 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 705314b2..4bd12538 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ *.o tags -include/loglevels.h include/GENERATED_*.h -loglevels.tmp *.swp *.gcda *.gcno diff --git a/Makefile b/Makefile index 065cdbcb..b56f696a 100644 --- a/Makefile +++ b/Makefile @@ -6,25 +6,15 @@ include $(TOPDIR)/common.mk AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c FILES:=$(filter-out $(AUTOGENERATED),$(wildcard src/*.c)) FILES:=$(FILES:.c=.o) -HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h)) +HEADERS:=$(wildcard include/*.h) CMDPARSE_HEADERS:=include/GENERATED_call.h include/GENERATED_enums.h include/GENERATED_tokens.h -# Recursively generate loglevels.h by explicitly calling make -# We need this step because we need to ensure that loglevels.h will be -# updated if necessary, but we also want to save rebuilds of the object -# files, so we cannot let the object files depend on loglevels.h. -ifeq ($(MAKECMDGOALS),loglevels.h) -#UNUSED:=$(warning Generating loglevels.h) -else -UNUSED:=$(shell $(MAKE) loglevels.h) -endif - SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log # Depend on the specific file (.c for each .o) and on all headers src/%.o: src/%.c ${HEADERS} echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $< + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< all: i3 subdirs @@ -42,18 +32,6 @@ subdirs: $(MAKE) -C $$dir; \ done -loglevels.h: - echo "[i3] LOGLEVELS" - for file in $$(ls src/*.c src/*.y src/*.l | grep -v 'cfgparse.\(tab\|yy\).c'); \ - do \ - echo $$(basename $$file .c); \ - done > loglevels.tmp - (echo "char *loglevels[] = {"; for file in $$(cat loglevels.tmp); \ - do \ - echo "\"$$file\", "; \ - done; \ - echo "};") > include/loglevels.h; - # The GENERATED_* files are actually all created from a single pass, so all # files just depend on the first one. include/GENERATED_call.h: generate-command-parser.pl parser-specs/commands.spec @@ -67,19 +45,19 @@ include/GENERATED_tokens.h: include/GENERATED_call.h # and once as an object file for i3. src/commands_parser.o: src/commands_parser.c ${HEADERS} ${CMDPARSE_HEADERS} echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -o test.commands_parser $< $(LIBS) - $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $< + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS} echo "[i3] LEX $<" $(FLEX) -i -o$(@:.o=.c) $< - $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c) src/cfgparse.y.o: src/cfgparse.y ${HEADERS} echo "[i3] YACC $<" $(BISON) --debug --verbose -b $(basename $< .y) -d $< - $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c) install: all @@ -129,7 +107,7 @@ dist: distclean rm -rf i3-${VERSION} clean: - rm -f src/*.o src/*.gcno src/cmdparse.* src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} loglevels.tmp include/loglevels.h include/GENERATED_* + rm -f src/*.o src/*.gcno src/cmdparse.* src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} include/GENERATED_* (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true $(MAKE) -C libi3 clean $(MAKE) -C docs clean diff --git a/docs/hacking-howto b/docs/hacking-howto index 73ae9633..7f2c35e6 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -141,7 +141,7 @@ src/load_layout.c:: Contains code for loading layouts from JSON files. src/log.c:: -Handles the setting of loglevels, contains the logging functions. +Contains the logging functions. src/main.c:: Initializes the window manager. diff --git a/include/log.h b/include/log.h index e5e20dc1..11555ab7 100644 --- a/include/log.h +++ b/include/log.h @@ -4,7 +4,7 @@ * i3 - an improved dynamic tiling window manager * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) * - * log.c: Setting of loglevels, logging functions. + * log.c: Logging functions. * */ #ifndef _LOG_H @@ -17,9 +17,8 @@ is, delete the preceding comma */ #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__) -#define DLOG(fmt, ...) debuglog(LOGLEVEL, "%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) -extern char *loglevels[]; extern char *errorfilename; extern char *shmlogname; extern int shmlog_size; @@ -32,10 +31,10 @@ extern int shmlog_size; void init_logging(void); /** - * Enables the given loglevel. + * Set debug logging. * */ -void add_loglevel(const char *level); +void set_debug_logging(const bool _debug_logging); /** * Set verbosity of i3. If verbose is set to true, informative messages will @@ -47,10 +46,10 @@ void set_verbosity(bool _verbose); /** * Logs the given message to stdout while prefixing the current time to it, - * but only if the corresponding debug loglevel was activated. + * but only if debug logging was activated. * */ -void debuglog(uint64_t lev, char *fmt, ...); +void debuglog(char *fmt, ...); /** * Logs the given message to stdout while prefixing the current time to it. diff --git a/include/regex.h b/include/regex.h index d55bb6cb..fe1e9f95 100644 --- a/include/regex.h +++ b/include/regex.h @@ -31,7 +31,7 @@ void regex_free(struct regex *regex); /** * Checks if the given regular expression matches the given input and returns * true if it does. In either case, it logs the outcome using LOG(), so it will - * be visible without any debug loglevel. + * be visible without debug logging. * */ bool regex_matches(struct regex *regex, const char *input); diff --git a/man/i3-dump-log.man b/man/i3-dump-log.man index 8e9094ff..eb8ba2f7 100644 --- a/man/i3-dump-log.man +++ b/man/i3-dump-log.man @@ -14,7 +14,7 @@ i3-dump-log [-s ] == DESCRIPTION Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store -full debug loglevel log output. This is extremely helpful for bugreports and +full debug log output. This is extremely helpful for bugreports and figuring out what is going on, without permanently logging to a file. With i3-dump-log, you can dump the SHM log to stdout. diff --git a/man/i3.man b/man/i3.man index 9d34c710..c20e39da 100644 --- a/man/i3.man +++ b/man/i3.man @@ -9,7 +9,7 @@ i3 - an improved dynamic, tiling window manager == SYNOPSIS -i3 [-a] [-c configfile] [-C] [-d ] [-v] [-V] +i3 [-a] [-c configfile] [-C] [-d all] [-v] [-V] == OPTIONS @@ -22,8 +22,9 @@ Specifies an alternate configuration file path. -C:: Check the configuration file for validity and exit. --d:: -Specifies the debug loglevel. To see the most output, use -d all. +-d all:: +Enables debug logging. +The 'all' parameter is present for historical reasons. -v:: Display version number (and date of the last commit). diff --git a/src/commands_parser.c b/src/commands_parser.c index 73a14565..e505c944 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -427,11 +427,11 @@ struct CommandResult *parse_command(const char *input) { /* * Logs the given message to stdout while prefixing the current time to it, - * but only if the corresponding debug loglevel was activated. + * but only if debug logging was activated. * This is to be called by DLOG() which includes filename/linenumber * */ -void debuglog(uint64_t lev, char *fmt, ...) { +void debuglog(char *fmt, ...) { va_list args; va_start(args, fmt); diff --git a/src/log.c b/src/log.c index 92e8f57c..d6f277f2 100644 --- a/src/log.c +++ b/src/log.c @@ -4,7 +4,7 @@ * i3 - an improved dynamic tiling window manager * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) * - * log.c: Setting of loglevels, logging functions. + * log.c: Logging functions. * */ #include @@ -29,10 +29,7 @@ #include "libi3.h" #include "shmlog.h" -/* loglevels.h is autogenerated at make time */ -#include "loglevels.h" - -static uint64_t loglevel = 0; +static bool debug_logging = false; static bool verbose = false; static FILE *errorfile; char *errorfilename; @@ -146,26 +143,11 @@ void set_verbosity(bool _verbose) { } /* - * Enables the given loglevel. + * Set debug logging. * */ -void add_loglevel(const char *level) { - /* Handle the special loglevel "all" */ - if (strcasecmp(level, "all") == 0) { - loglevel = UINT64_MAX; - return; - } - - for (int i = 0; i < sizeof(loglevels) / sizeof(char*); i++) { - if (strcasecmp(loglevels[i], level) != 0) - continue; - - /* The position in the array (plus one) is the amount of times - * which we need to shift 1 to the left to get our bitmask for - * the specific loglevel. */ - loglevel |= (1 << (i+1)); - break; - } +void set_debug_logging(const bool _debug_logging) { + debug_logging = _debug_logging; } /* @@ -271,17 +253,17 @@ void errorlog(char *fmt, ...) { /* * Logs the given message to stdout while prefixing the current time to it, - * but only if the corresponding debug loglevel was activated. + * but only if debug logging was activated. * This is to be called by DLOG() which includes filename/linenumber * */ -void debuglog(uint64_t lev, char *fmt, ...) { +void debuglog(char *fmt, ...) { va_list args; - if (!logbuffer && !(loglevel & lev)) + if (!logbuffer && !(debug_logging)) return; va_start(args, fmt); - vlog((loglevel & lev), fmt, args); + vlog(debug_logging, fmt, args); va_end(args); } diff --git a/src/main.c b/src/main.c index e332f5b4..b8e2e08c 100644 --- a/src/main.c +++ b/src/main.c @@ -318,8 +318,8 @@ int main(int argc, char *argv[]) { set_verbosity(true); break; case 'd': - LOG("Enabling debug loglevel %s\n", optarg); - add_loglevel(optarg); + LOG("Enabling debug logging\n"); + set_debug_logging(true); break; case 'l': /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */ @@ -367,12 +367,12 @@ int main(int argc, char *argv[]) { } /* fall-through */ default: - fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]); + fprintf(stderr, "Usage: %s [-c configfile] [-d all] [-a] [-v] [-V] [-C]\n", argv[0]); fprintf(stderr, "\n"); fprintf(stderr, "\t-a disable autostart ('exec' lines in config)\n"); fprintf(stderr, "\t-c use the provided configfile instead\n"); fprintf(stderr, "\t-C validate configuration file and exit\n"); - fprintf(stderr, "\t-d enable debug output with the specified loglevel\n"); + fprintf(stderr, "\t-d all enable debug output\n"); fprintf(stderr, "\t-L path to the serialized layout during restarts\n"); fprintf(stderr, "\t-v display version and exit\n"); fprintf(stderr, "\t-V enable verbose mode\n"); diff --git a/src/regex.c b/src/regex.c index a0b51f66..e5698d02 100644 --- a/src/regex.c +++ b/src/regex.c @@ -67,7 +67,7 @@ void regex_free(struct regex *regex) { /* * Checks if the given regular expression matches the given input and returns * true if it does. In either case, it logs the outcome using LOG(), so it will - * be visible without any debug loglevel. + * be visible without debug logging. * */ bool regex_matches(struct regex *regex, const char *input) { From 97d857a5d06ba30c071815011939d7b7af11480c Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:47:15 +0200 Subject: [PATCH 019/200] commom.mk: Support V make variable (verbose build) --- common.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common.mk b/common.mk index 43949059..241576fc 100644 --- a/common.mk +++ b/common.mk @@ -140,8 +140,11 @@ CFLAGS += -fprofile-arcs -ftest-coverage LIBS += -lgcov endif +V ?= 0 +ifeq ($(V),0) # Don’t print command lines which are run .SILENT: +endif # Always remake the following targets .PHONY: install clean dist distclean From 8853334bbefd57f8f9bd6bdeb0c221616b40b446 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:19:12 +0200 Subject: [PATCH 020/200] New Makefile layout, move i3 to it --- Makefile | 74 +++++++++---------------------------------------------- src/i3.mk | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 63 deletions(-) create mode 100644 src/i3.mk diff --git a/Makefile b/Makefile index b56f696a..c121bbdf 100644 --- a/Makefile +++ b/Makefile @@ -2,25 +2,18 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -# Depend on the object files of all source-files in src/*.c and on all header files -AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c -FILES:=$(filter-out $(AUTOGENERATED),$(wildcard src/*.c)) -FILES:=$(FILES:.c=.o) -HEADERS:=$(wildcard include/*.h) -CMDPARSE_HEADERS:=include/GENERATED_call.h include/GENERATED_enums.h include/GENERATED_tokens.h - SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log -# Depend on the specific file (.c for each .o) and on all headers -src/%.o: src/%.c ${HEADERS} - echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< +ALL_TARGETS = +INSTALL_TARGETS = +CLEAN_TARGETS = +DISTCLEAN_TARGETS = -all: i3 subdirs +all: real-all -i3: libi3/libi3.a src/cfgparse.y.o src/cfgparse.yy.o ${FILES} - echo "[i3] LINK i3" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) +include src/i3.mk + +real-all: $(ALL_TARGETS) subdirs libi3/%.a: libi3/*.c $(MAKE) -C libi3 @@ -32,51 +25,7 @@ subdirs: $(MAKE) -C $$dir; \ done -# The GENERATED_* files are actually all created from a single pass, so all -# files just depend on the first one. -include/GENERATED_call.h: generate-command-parser.pl parser-specs/commands.spec - echo "[i3] Generating command parser" - (cd include; ../generate-command-parser.pl) -include/GENERATED_enums.h: include/GENERATED_call.h -include/GENERATED_tokens.h: include/GENERATED_call.h - -# This target compiles the command parser twice: -# Once with -DTEST_PARSER, creating a stand-alone executable used for tests, -# and once as an object file for i3. -src/commands_parser.o: src/commands_parser.c ${HEADERS} ${CMDPARSE_HEADERS} - echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS} - echo "[i3] LEX $<" - $(FLEX) -i -o$(@:.o=.c) $< - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c) - - -src/cfgparse.y.o: src/cfgparse.y ${HEADERS} - echo "[i3] YACC $<" - $(BISON) --debug --verbose -b $(basename $< .y) -d $< - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c) - - -install: all - echo "[i3] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3 - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3 - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications - $(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/ - $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/ - test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config - test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes - $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop - $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop - $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/ +install: $(INSTALL_TARGETS) for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir install; \ done @@ -106,8 +55,7 @@ dist: distclean tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION} rm -rf i3-${VERSION} -clean: - rm -f src/*.o src/*.gcno src/cmdparse.* src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} include/GENERATED_* +clean: $(CLEAN_TARGETS) (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true $(MAKE) -C libi3 clean $(MAKE) -C docs clean @@ -118,7 +66,7 @@ clean: $(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \ done -distclean: clean +distclean: clean $(DISTCLEAN_TARGETS) rm -f i3 for dir in $(SUBDIRS); do \ echo ""; \ diff --git a/src/i3.mk b/src/i3.mk new file mode 100644 index 00000000..f3c0193e --- /dev/null +++ b/src/i3.mk @@ -0,0 +1,61 @@ +ALL_TARGETS += i3 +INSTALL_TARGETS += install-i3 +CLEAN_TARGETS += clean-i3 + +i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c +i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) +i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) +i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) + +i3_OBJECTS = $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) + +src/%.o: src/%.c $(i3_HEADERS) + echo "[i3] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS) + echo "[i3] LEX $<" + $(FLEX) -i -o $@ $< + +src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS) + echo "[i3] YACC $<" + $(BISON) --debug --verbose -b $(basename $< .y) -d $< + +# This target compiles the command parser twice: +# Once with -DTEST_PARSER, creating a stand-alone executable used for tests, +# and once as an object file for i3. +src/commands_parser.o: src/commands_parser.c $(i3_HEADERS) i3-command-parser.stamp + echo "[i3] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec + echo "[i3] Generating command parser" + (cd include; ../generate-command-parser.pl) + touch $@ + +i3: libi3.a $(i3_OBJECTS) + echo "[i3] Link i3" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3: i3 + echo "[i3] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3 + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3 + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications + $(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/ + test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config + test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes + $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop + $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop + $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/ + +clean-i3: + echo "[i3] Clean" + rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} From e3b9ffe3813cca5bc762cbb8a2179cfd03dbedf9 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 021/200] Move libi3 to the new Makefile layout --- Makefile | 5 +---- common.mk | 2 +- libi3/Makefile | 26 -------------------------- libi3/libi3.mk | 19 +++++++++++++++++++ src/i3.mk | 2 +- 5 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 libi3/Makefile create mode 100644 libi3/libi3.mk diff --git a/Makefile b/Makefile index c121bbdf..2ffdffcc 100644 --- a/Makefile +++ b/Makefile @@ -11,13 +11,11 @@ DISTCLEAN_TARGETS = all: real-all +include libi3/libi3.mk include src/i3.mk real-all: $(ALL_TARGETS) subdirs -libi3/%.a: libi3/*.c - $(MAKE) -C libi3 - subdirs: for dir in $(SUBDIRS); do \ echo ""; \ @@ -57,7 +55,6 @@ dist: distclean clean: $(CLEAN_TARGETS) (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true - $(MAKE) -C libi3 clean $(MAKE) -C docs clean $(MAKE) -C man clean for dir in $(SUBDIRS); do \ diff --git a/common.mk b/common.mk index 241576fc..25e81200 100644 --- a/common.mk +++ b/common.mk @@ -72,7 +72,7 @@ LIBS += -lm ifneq ($(UNAME),Darwin) LIBS += -lrt endif -LIBS += -L $(TOPDIR)/libi3 -li3 +LIBS += -L $(TOPDIR) -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) diff --git a/libi3/Makefile b/libi3/Makefile deleted file mode 100644 index e9efcf7b..00000000 --- a/libi3/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Default value so one can compile i3-msg standalone -TOPDIR=.. - -include $(TOPDIR)/common.mk - -CFLAGS += -I$(TOPDIR)/include - -# Depend on the object files of all source-files in src/*.c and on all header files -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) -HEADERS=$(wildcard *.h) - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[libi3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: libi3.a - -libi3.a: ${FILES} - echo "[libi3] AR libi3.a" - ar rcs libi3.a ${FILES} - -clean: - rm -f *.o libi3.a - -distclean: clean diff --git a/libi3/libi3.mk b/libi3/libi3.mk new file mode 100644 index 00000000..9c9f3c91 --- /dev/null +++ b/libi3/libi3.mk @@ -0,0 +1,19 @@ +CLEAN_TARGETS += clean-libi3 + +libi3_SOURCES := $(wildcard libi3/*.c) +libi3_HEADERS := $(wildcard libi3/*.h) + +libi3_OBJECTS := $(libi3_SOURCES:.c=.o) + + +libi3/%.o: libi3/%.c $(libi3_HEADERS) + echo "[libi3] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +libi3.a: $(libi3_OBJECTS) + echo "[libi3] AR libi3.a" + ar rcs $@ $^ + +clean-libi3: + echo "[libi3] Clean" + rm -f $(libi3_OBJECTS) libi3.a diff --git a/src/i3.mk b/src/i3.mk index f3c0193e..026bf4a0 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -7,7 +7,7 @@ i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_OBJECTS = $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) +i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) src/%.o: src/%.c $(i3_HEADERS) echo "[i3] CC $<" From 0c2e4d8347dd440cd1d6d86e5b050af931966a0d Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 022/200] Move i3-config-wizard to the new Makefile layout --- Makefile | 3 ++- i3-config-wizard/i3-config-wizard.mk | 35 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 i3-config-wizard/i3-config-wizard.mk diff --git a/Makefile b/Makefile index 2ffdffcc..3f434427 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log +SUBDIRS:=i3-msg i3-input i3-nagbar i3bar i3-dump-log ALL_TARGETS = INSTALL_TARGETS = @@ -13,6 +13,7 @@ all: real-all include libi3/libi3.mk include src/i3.mk +include i3-config-wizard/i3-config-wizard.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk new file mode 100644 index 00000000..ed41941b --- /dev/null +++ b/i3-config-wizard/i3-config-wizard.mk @@ -0,0 +1,35 @@ +ALL_TARGETS += i3-config-wizard/i3-config-wizard +INSTALL_TARGETS += install-i3-config-wizard +CLEAN_TARGETS += clean-i3-config-wizard + +i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c +i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) +i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) + +i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) + + +i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS) + echo "[i3-config-wizard] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS) + echo "[i3-config-wizard] LEX $<" + $(FLEX) -i -o $@ $< + +i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_HEADERS) + echo "[i3-config-wizard] YACC $<" + $(BISON) --debug --verbose -b $(basename $< .y) -d $< + +i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS) + echo "[i3-config-wizard] Link i3-config-wizard" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3-config-wizard: i3-config-wizard/i3-config-wizard + echo "[i3-config-wizard] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(PREFIX)/bin/ + +clean-i3-config-wizard: + echo "[i3-config-wizard] Clean" + rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.{output,dot} From bbede0ba00dc6e8b1e50233d00167defc0f4b8de Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 023/200] Move i3-msg to the new Makefile layout --- Makefile | 3 ++- i3-msg/i3-msg.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 i3-msg/i3-msg.mk diff --git a/Makefile b/Makefile index 3f434427..5ed79c35 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3-msg i3-input i3-nagbar i3bar i3-dump-log +SUBDIRS:=i3-input i3-nagbar i3bar i3-dump-log ALL_TARGETS = INSTALL_TARGETS = @@ -14,6 +14,7 @@ all: real-all include libi3/libi3.mk include src/i3.mk include i3-config-wizard/i3-config-wizard.mk +include i3-msg/i3-msg.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk new file mode 100644 index 00000000..9526dc04 --- /dev/null +++ b/i3-msg/i3-msg.mk @@ -0,0 +1,26 @@ +ALL_TARGETS += i3-msg/i3-msg +INSTALL_TARGETS += install-i3-msg +CLEAN_TARGETS += clean-i3-msg + +i3_msg_SOURCES := $(wildcard i3-msg/*.c) +i3_msg_HEADERS := $(wildcard i3-msg/*.h) + +i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) + + +i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) + echo "[i3-msg] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) + echo "[i3-msg] Link i3-msg" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3-msg: i3-msg/i3-msg + echo "[i3-msg] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(PREFIX)/bin/ + +clean-i3-msg: + echo "[i3-msg] Clean" + rm -f $(i3_msg_OBJECTS) i3-msg/i3-msg From eb08d336927430ed35091110bfad29f7031d8acb Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 024/200] Move i3-input to the new Makefile layout --- Makefile | 3 ++- i3-input/i3-input.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 i3-input/i3-input.mk diff --git a/Makefile b/Makefile index 5ed79c35..cb1bb77c 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3-input i3-nagbar i3bar i3-dump-log +SUBDIRS:=i3-nagbar i3bar i3-dump-log ALL_TARGETS = INSTALL_TARGETS = @@ -15,6 +15,7 @@ include libi3/libi3.mk include src/i3.mk include i3-config-wizard/i3-config-wizard.mk include i3-msg/i3-msg.mk +include i3-input/i3-input.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk new file mode 100644 index 00000000..35c74964 --- /dev/null +++ b/i3-input/i3-input.mk @@ -0,0 +1,26 @@ +ALL_TARGETS += i3-input/i3-input +INSTALL_TARGETS += install-i3-input +CLEAN_TARGETS += clean-i3-input + +i3_input_SOURCES := $(wildcard i3-input/*.c) +i3_input_HEADERS := $(wildcard i3-input/*.h) + +i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) + + +i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) + echo "[i3-input] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-input/i3-input: libi3.a $(i3_input_OBJECTS) + echo "[i3-input] Link i3-input" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3-input: i3-input/i3-input + echo "[i3-input] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(PREFIX)/bin/ + +clean-i3-input: + echo "[i3-input] Clean" + rm -f $(i3_input_OBJECTS) i3-input/i3-input From 8910a138ff44fdf818c291fc020e471e750ca762 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 025/200] Move i3-nagbar to the new Makefile layout --- Makefile | 3 ++- i3-nagbar/i3-nagbar.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 i3-nagbar/i3-nagbar.mk diff --git a/Makefile b/Makefile index cb1bb77c..b6f6b89e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3-nagbar i3bar i3-dump-log +SUBDIRS:=i3bar i3-dump-log ALL_TARGETS = INSTALL_TARGETS = @@ -16,6 +16,7 @@ include src/i3.mk include i3-config-wizard/i3-config-wizard.mk include i3-msg/i3-msg.mk include i3-input/i3-input.mk +include i3-nagbar/i3-nagbar.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk new file mode 100644 index 00000000..9677b808 --- /dev/null +++ b/i3-nagbar/i3-nagbar.mk @@ -0,0 +1,26 @@ +ALL_TARGETS += i3-nagbar/i3-nagbar +INSTALL_TARGETS += install-i3-nagbar +CLEAN_TARGETS += clean-i3-nagbar + +i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) +i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) + +i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) + + +i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) + echo "[i3-nagbar] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) + echo "[i3-nagbar] Link i3-nagbar" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3-nagbar: i3-nagbar/i3-nagbar + echo "[i3-nagbar] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(PREFIX)/bin/ + +clean-i3-nagbar: + echo "[i3-nagbar] Clean" + rm -f $(i3_nagbar_OBJECTS) i3-nagbar/i3-nagbar From 1a1eb0f3f2fec380abdb55dcc8ec2bdcc1bca6cc Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 026/200] Move i3bar to the new Makefile layout --- Makefile | 3 ++- i3bar/i3bar.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 i3bar/i3bar.mk diff --git a/Makefile b/Makefile index b6f6b89e..f8f7245d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3bar i3-dump-log +SUBDIRS:=i3-dump-log ALL_TARGETS = INSTALL_TARGETS = @@ -17,6 +17,7 @@ include i3-config-wizard/i3-config-wizard.mk include i3-msg/i3-msg.mk include i3-input/i3-input.mk include i3-nagbar/i3-nagbar.mk +include i3bar/i3bar.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk new file mode 100644 index 00000000..3f16f8a2 --- /dev/null +++ b/i3bar/i3bar.mk @@ -0,0 +1,26 @@ +ALL_TARGETS += i3bar/i3bar +INSTALL_TARGETS += install-i3bar +CLEAN_TARGETS += clean-i3bar + +i3bar_SOURCES := $(wildcard i3bar/src/*.c) +i3bar_HEADERS := $(wildcard i3bar/include/*.h) + +i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) + + +i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) + echo "[i3bar] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< + +i3bar/i3bar: libi3.a $(i3bar_OBJECTS) + echo "[i3bar] Link i3bar" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3bar: i3bar/i3bar + echo "[i3bar] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(PREFIX)/bin/ + +clean-i3bar: + echo "[i3bar] Clean" + rm -f $(i3bar_OBJECTS) i3bar/i3bar From 4c0b519976f2772e0920d7cc429955bf216a38a8 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 16:22:57 +0200 Subject: [PATCH 027/200] Move i3-dump-log to the new Makefile layout --- Makefile | 3 ++- i3-dump-log/i3-dump-log.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 i3-dump-log/i3-dump-log.mk diff --git a/Makefile b/Makefile index f8f7245d..5663342f 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TOPDIR=$(shell pwd) include $(TOPDIR)/common.mk -SUBDIRS:=i3-dump-log +SUBDIRS:= ALL_TARGETS = INSTALL_TARGETS = @@ -18,6 +18,7 @@ include i3-msg/i3-msg.mk include i3-input/i3-input.mk include i3-nagbar/i3-nagbar.mk include i3bar/i3bar.mk +include i3-dump-log/i3-dump-log.mk real-all: $(ALL_TARGETS) subdirs diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk new file mode 100644 index 00000000..a29bced4 --- /dev/null +++ b/i3-dump-log/i3-dump-log.mk @@ -0,0 +1,26 @@ +ALL_TARGETS += i3-dump-log/i3-dump-log +INSTALL_TARGETS += install-i3-dump-log +CLEAN_TARGETS += clean-i3-dump-log + +i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) +i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) + +i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) + + +i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) + echo "[i3-dump-log] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) + echo "[i3-dump-log] Link i3-dump-log" + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + +install-i3-dump-log: i3-dump-log/i3-dump-log + echo "[i3-dump-log] Install" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(PREFIX)/bin/ + +clean-i3-dump-log: + echo "[i3-dump-log] Clean" + rm -f $(i3_dump_log_OBJECTS) i3-dump-log/i3-dump-log From 420019f2d0c88662e97348f3a02023b94f85ffd6 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 17:30:09 +0200 Subject: [PATCH 028/200] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4bd12538..3eac9270 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,5 @@ i3-nagbar/i3-nagbar i3-msg/i3-msg i3-config-wizard/i3-config-wizard i3-dump-log/i3-dump-log -libi3/libi3.a +libi3.a docs/*.pdf From 46713c9e6d63f3a8396f43296acab6a5d11e5707 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 16 May 2012 17:32:25 +0200 Subject: [PATCH 029/200] Makefile: Remove SUBDIRS --- Makefile | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 5663342f..ba0ed002 100644 --- a/Makefile +++ b/Makefile @@ -20,19 +20,9 @@ include i3-nagbar/i3-nagbar.mk include i3bar/i3bar.mk include i3-dump-log/i3-dump-log.mk -real-all: $(ALL_TARGETS) subdirs - -subdirs: - for dir in $(SUBDIRS); do \ - echo ""; \ - echo "MAKE $$dir"; \ - $(MAKE) -C $$dir; \ - done +real-all: $(ALL_TARGETS) install: $(INSTALL_TARGETS) - for dir in $(SUBDIRS); do \ - $(MAKE) -C $$dir install; \ - done dist: distclean [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION} @@ -63,19 +53,8 @@ clean: $(CLEAN_TARGETS) (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true $(MAKE) -C docs clean $(MAKE) -C man clean - for dir in $(SUBDIRS); do \ - echo ""; \ - echo "CLEAN $$dir"; \ - $(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \ - done distclean: clean $(DISTCLEAN_TARGETS) - rm -f i3 - for dir in $(SUBDIRS); do \ - echo ""; \ - echo "DISTCLEAN $$dir"; \ - $(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \ - done coverage: rm -f /tmp/i3-coverage.info From 66adb62e1fde8b8e796399895a4f2e5b4e2b8f1c Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 17 Jun 2012 14:31:45 +0200 Subject: [PATCH 030/200] Move docs to the new Makefile layout --- Makefile | 4 ++-- common.mk | 3 +++ docs/docs.mk | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 docs/docs.mk diff --git a/Makefile b/Makefile index ba0ed002..5374527d 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ include i3-input/i3-input.mk include i3-nagbar/i3-nagbar.mk include i3bar/i3bar.mk include i3-dump-log/i3-dump-log.mk +include docs/docs.mk real-all: $(ALL_TARGETS) @@ -33,7 +34,7 @@ dist: distclean # Only copy toplevel documentation (important stuff) mkdir i3-${VERSION}/docs # Pre-generate documentation - $(MAKE) -C docs + $(MAKE) docs $(MAKE) -C i3bar/doc # Cleanup τεχ output files find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \; @@ -51,7 +52,6 @@ dist: distclean clean: $(CLEAN_TARGETS) (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true - $(MAKE) -C docs clean $(MAKE) -C man clean distclean: clean $(DISTCLEAN_TARGETS) diff --git a/common.mk b/common.mk index 25e81200..2f550cff 100644 --- a/common.mk +++ b/common.mk @@ -144,6 +144,9 @@ V ?= 0 ifeq ($(V),0) # Don’t print command lines which are run .SILENT: + +# echo-ing vars +V_ASCIIDOC = echo ASCIIDOC $@; endif # Always remake the following targets diff --git a/docs/docs.mk b/docs/docs.mk new file mode 100644 index 00000000..773c8322 --- /dev/null +++ b/docs/docs.mk @@ -0,0 +1,35 @@ +DISTCLEAN_TARGETS += clean-docs + +# To pass additional parameters for asciidoc +ASCIIDOC = asciidoc + +ASCIIDOC_NOTOC_TARGETS = \ + docs/debugging.html \ + docs/debugging-release-version.html + +ASCIIDOC_TOC_TARGETS = \ + docs/hacking-howto.html \ + docs/userguide.html \ + docs/ipc.html \ + docs/multi-monitor.html \ + docs/wsbar.html \ + docs/testsuite.html \ + docs/i3bar-protocol.html + +ASCIIDOC_TARGETS = \ + $(ASCIIDOC_TOC_TARGETS) \ + $(ASCIIDOC_NOTOC_TARGETS) + +ASCIIDOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -n $(ASCIIDOC_FLAGS) -o $@ $< +ASCIIDOC_TOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -a toc -n $(ASCIIDOC_FLAGS) -o $@ $< + +docs: $(ASCIIDOC_TARGETS) + +$(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/% + $(ASCIIDOC_TOC_CALL) + +$(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/% + $(ASCIIDOC_CALL) + +clean-docs: + rm -f $(ASCIIDOC_TARGETS) From e452acb30e8253a3a4b53233f22b31460cb02aec Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 17 Jun 2012 14:37:07 +0200 Subject: [PATCH 031/200] Add stub Makefiles to allow subdir make calls --- docs/Makefile | 39 ++++--------------------------- i3-config-wizard/Makefile | 49 +++++---------------------------------- i3-dump-log/Makefile | 34 +++++---------------------- i3-input/Makefile | 37 +++++------------------------ i3-msg/Makefile | 34 +++++---------------------- i3-nagbar/Makefile | 37 +++++------------------------ i3bar/Makefile | 44 +++++------------------------------ libi3/Makefile | 7 ++++++ src/Makefile | 10 ++++++++ 9 files changed, 58 insertions(+), 233 deletions(-) create mode 100644 libi3/Makefile create mode 100644 src/Makefile diff --git a/docs/Makefile b/docs/Makefile index fc41236f..d6e670c9 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,36 +1,7 @@ -# To pass additional parameters for asciidoc -ASCIIDOC=asciidoc - -ASCIIDOC_TARGETS:=hacking-howto.html debugging.html debugging-release-version.html userguide.html ipc.html multi-monitor.html wsbar.html testsuite.html i3bar-protocol.html - -all: ${ASCIIDOC_TARGETS} - -hacking-howto.html: hacking-howto - $(ASCIIDOC) -a toc -n $< - -i3bar-protocol.html: i3bar-protocol - $(ASCIIDOC) -a toc -n $< - -debugging.html: debugging - $(ASCIIDOC) -n $< - -debugging-release-version.html: debugging-release-version - $(ASCIIDOC) -n $< - -userguide.html: userguide - $(ASCIIDOC) -a toc -n $< - -testsuite.html: testsuite - $(ASCIIDOC) -a toc -n $< - -ipc.html: ipc - $(ASCIIDOC) -a toc -n $< - -multi-monitor.html: multi-monitor - $(ASCIIDOC) -a toc -n $< - -wsbar.html: wsbar - $(ASCIIDOC) -a toc -n $< +all: + $(MAKE) -C .. docs clean: - rm -f ${ASCIIDOC_TARGETS} + $(MAKE) -C .. clean-docs + +.PHONY: all clean diff --git a/i3-config-wizard/Makefile b/i3-config-wizard/Makefile index 75d4684f..d5ac18c6 100644 --- a/i3-config-wizard/Makefile +++ b/i3-config-wizard/Makefile @@ -1,47 +1,10 @@ -# Default value so one can compile i3-input standalone -TOPDIR=.. +all: + $(MAKE) -C .. i3-config-wizard/i3-config-wizard -include $(TOPDIR)/common.mk - -# Depend on the object files of all source-files in src/*.c and on all header files -AUTOGENERATED:=cfgparse.tab.c cfgparse.yy.c -FILES:=$(patsubst %.c,%.o,$(filter-out $(AUTOGENERATED),$(wildcard *.c))) -HEADERS:=$(wildcard *.h) - -CPPFLAGS += -I$(TOPDIR)/include - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[i3-config-wizard] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: i3-config-wizard - -i3-config-wizard: $(TOPDIR)/libi3/libi3.a cfgparse.y.o cfgparse.yy.o ${FILES} - echo "[i3-config-wizard] LINK i3-config-wizard" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) - -$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c - $(MAKE) -C $(TOPDIR)/libi3 - -cfgparse.yy.o: cfgparse.l cfgparse.y.o ${HEADERS} - echo "[i3-config-wizard] LEX $<" - $(FLEX) -i -o$(@:.o=.c) $< - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c) - -cfgparse.y.o: cfgparse.y ${HEADERS} - echo "[i3-config-wizard] YACC $<" - $(BISON) --debug --verbose -b $(basename $< .y) -d $< - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c) - - -install: all - echo "[i3-config-wizard] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3-config-wizard $(DESTDIR)$(PREFIX)/bin/ +install: + $(MAKE) -C .. install-i3-config-wizard clean: - rm -f *.o cfgparse.tab.{c,h} cfgparse.output cfgparse.yy.c + $(MAKE) -C .. clean-i3-config-wizard -distclean: clean - rm -f i3-config-wizard +.PHONY: all install clean diff --git a/i3-dump-log/Makefile b/i3-dump-log/Makefile index 18076e51..2b9e4fe9 100644 --- a/i3-dump-log/Makefile +++ b/i3-dump-log/Makefile @@ -1,32 +1,10 @@ -# Default value so one can compile i3-dump-log standalone -TOPDIR=.. +all: + $(MAKE) -C .. i3-dump-log/i3-dump-log -include $(TOPDIR)/common.mk - -CFLAGS += -I$(TOPDIR)/include - -# Depend on the object files of all source-files in src/*.c and on all header files -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) -HEADERS=$(wildcard *.h) - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[i3-dump-log] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: i3-dump-log - -i3-dump-log: ${FILES} - echo "[i3-dump-log] LINK i3-dump-log" - $(CC) $(LDFLAGS) -o i3-dump-log ${FILES} $(LIBS) - -install: all - echo "[i3-dump-log] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3-dump-log $(DESTDIR)$(PREFIX)/bin/ +install: + $(MAKE) -C .. install-i3-dump-log clean: - rm -f *.o + $(MAKE) -C .. clean-i3-dump-log -distclean: clean - rm -f i3-dump-log +.PHONY: all install clean diff --git a/i3-input/Makefile b/i3-input/Makefile index 493df784..e1c8eae5 100644 --- a/i3-input/Makefile +++ b/i3-input/Makefile @@ -1,35 +1,10 @@ -# Default value so one can compile i3-input standalone -TOPDIR=.. +all: + $(MAKE) -C .. i3-input/i3-input -include $(TOPDIR)/common.mk - -CPPFLAGS += -I$(TOPDIR)/include - -# Depend on the object files of all source-files in src/*.c and on all header files -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) -HEADERS=$(wildcard *.h) - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[i3-input] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: i3-input - -i3-input: $(TOPDIR)/libi3/libi3.a ${FILES} - echo "[i3-input] LINK i3-input" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) - -$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c - $(MAKE) -C $(TOPDIR)/libi3 - -install: all - echo "[i3-input] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3-input $(DESTDIR)$(PREFIX)/bin/ +install: + $(MAKE) -C .. install-i3-input clean: - rm -f *.o + $(MAKE) -C .. clean-i3-input -distclean: clean - rm -f i3-input +.PHONY: all install clean diff --git a/i3-msg/Makefile b/i3-msg/Makefile index 617df932..fedb31e7 100644 --- a/i3-msg/Makefile +++ b/i3-msg/Makefile @@ -1,32 +1,10 @@ -# Default value so one can compile i3-msg standalone -TOPDIR=.. +all: + $(MAKE) -C .. i3-msg/i3-msg -include $(TOPDIR)/common.mk - -CFLAGS += -I$(TOPDIR)/include - -# Depend on the object files of all source-files in src/*.c and on all header files -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) -HEADERS=$(wildcard *.h) - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[i3-msg] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: i3-msg - -i3-msg: ${FILES} - echo "[i3-msg] LINK i3-msg" - $(CC) $(LDFLAGS) -o i3-msg ${FILES} $(LIBS) - -install: all - echo "[i3-msg] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3-msg $(DESTDIR)$(PREFIX)/bin/ +install: + $(MAKE) -C .. install-i3-msg clean: - rm -f *.o + $(MAKE) -C .. clean-i3-msg -distclean: clean - rm -f i3-msg +.PHONY: all install clean diff --git a/i3-nagbar/Makefile b/i3-nagbar/Makefile index 05a5b911..12748e21 100644 --- a/i3-nagbar/Makefile +++ b/i3-nagbar/Makefile @@ -1,35 +1,10 @@ -# Default value so one can compile i3-nagbar standalone -TOPDIR=.. +all: + $(MAKE) -C .. i3-nagbar/i3-nagbar -include $(TOPDIR)/common.mk - -CPPFLAGS += -I$(TOPDIR)/include - -# Depend on the object files of all source-files in src/*.c and on all header files -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) -HEADERS=$(wildcard *.h) - -# Depend on the specific file (.c for each .o) and on all headers -%.o: %.c ${HEADERS} - echo "[i3-nagbar] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -all: i3-nagbar - -i3-nagbar: $(TOPDIR)/libi3/libi3.a ${FILES} - echo "[i3-nagbar] LINK i3-nagbar" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) - -$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c - $(MAKE) -C $(TOPDIR)/libi3 - -install: all - echo "[i3-nagbar] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3-nagbar $(DESTDIR)$(PREFIX)/bin/ +install: + $(MAKE) -C .. install-i3-nagbar clean: - rm -f *.o + $(MAKE) -C .. clean-i3-nagbar -distclean: clean - rm -f i3-nagbar +.PHONY: all install clean diff --git a/i3bar/Makefile b/i3bar/Makefile index 79d0e7cd..cd23cd81 100644 --- a/i3bar/Makefile +++ b/i3bar/Makefile @@ -1,42 +1,10 @@ -TOPDIR=.. +all: + $(MAKE) -C .. i3bar/i3bar -include $(TOPDIR)/common.mk - -FILES:=$(wildcard src/*.c) -FILES:=$(FILES:.c=.o) -HEADERS:=$(wildcard include/*.h) - -CPPFLAGS += -I$(TOPDIR)/include - -all: i3bar doc - -i3bar: $(TOPDIR)/libi3/libi3.a ${FILES} - echo "[i3bar] LINK" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) - -$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c - $(MAKE) -C $(TOPDIR)/libi3 - -doc: - echo "" - echo "[i3bar] SUBDIR doc" - $(MAKE) -C doc - -src/%.o: src/%.c ${HEADERS} - echo "[i3bar] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< - -install: all - echo "[i3bar] INSTALL" - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin +install: + $(MAKE) -C .. install-i3bar clean: - rm -f src/*.o - $(MAKE) -C doc clean + $(MAKE) -C .. clean-i3bar -distclean: clean - rm -f i3bar - $(MAKE) -C doc distclean - -.PHONY: install clean distclean doc +.PHONY: all install clean diff --git a/libi3/Makefile b/libi3/Makefile new file mode 100644 index 00000000..2c2f68ad --- /dev/null +++ b/libi3/Makefile @@ -0,0 +1,7 @@ +all: + $(MAKE) -C .. libi3.a + +clean: + $(MAKE) -C .. clean-libi3 + +.PHONY: all clean diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..6a37f56c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,10 @@ +all: + $(MAKE) -C .. i3 + +install: + $(MAKE) -C .. install-i3 + +clean: + $(MAKE) -C .. clean-i3 + +.PHONY: all install clean From 8029fae6a900edb8e840cf11ce90543fa898e743 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 17 Jun 2012 14:52:41 +0200 Subject: [PATCH 032/200] Move mans to the new Makefile layout --- Makefile | 6 ++---- common.mk | 1 + i3bar/doc/Makefile | 9 --------- man/Makefile | 16 ++++------------ {i3bar/doc => man}/i3bar.man | 0 man/man.mk | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 39 insertions(+), 25 deletions(-) delete mode 100644 i3bar/doc/Makefile rename {i3bar/doc => man}/i3bar.man (100%) create mode 100644 man/man.mk diff --git a/Makefile b/Makefile index 5374527d..94c9c63e 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ include i3-nagbar/i3-nagbar.mk include i3bar/i3bar.mk include i3-dump-log/i3-dump-log.mk include docs/docs.mk +include man/man.mk real-all: $(ALL_TARGETS) @@ -35,7 +36,6 @@ dist: distclean mkdir i3-${VERSION}/docs # Pre-generate documentation $(MAKE) docs - $(MAKE) -C i3bar/doc # Cleanup τεχ output files find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \; find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \; @@ -44,15 +44,13 @@ dist: distclean find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; sed -e 's/^GIT_VERSION:=\(.*\)/GIT_VERSION:=$(shell /bin/echo '${GIT_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' common.mk > i3-${VERSION}/common.mk # Pre-generate a manpage to allow distributors to skip this step and save some dependencies - $(MAKE) -C man + $(MAKE) mans cp man/*.1 i3-${VERSION}/man/ - cp i3bar/doc/*.1 i3-${VERSION}/i3bar/doc/ tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION} rm -rf i3-${VERSION} clean: $(CLEAN_TARGETS) (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true - $(MAKE) -C man clean distclean: clean $(DISTCLEAN_TARGETS) diff --git a/common.mk b/common.mk index 2f550cff..941da7d2 100644 --- a/common.mk +++ b/common.mk @@ -147,6 +147,7 @@ ifeq ($(V),0) # echo-ing vars V_ASCIIDOC = echo ASCIIDOC $@; +V_A2X = echo A2X $@; endif # Always remake the following targets diff --git a/i3bar/doc/Makefile b/i3bar/doc/Makefile deleted file mode 100644 index 69566750..00000000 --- a/i3bar/doc/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -all: i3bar.1 - -i3bar.1: i3bar.man - echo "A2X i3bar" - a2x --no-xmllint -f manpage i3bar.man -clean: - rm -f i3bar.xml i3bar.1 i3bar.html - -distclean: clean diff --git a/man/Makefile b/man/Makefile index ff08dc57..e4cee0cc 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,15 +1,7 @@ -A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf" - -all: i3.1 i3-msg.1 i3-input.1 i3-nagbar.1 i3-config-wizard.1 i3-migrate-config-to-v4.1 i3-sensible-editor.1 i3-sensible-pager.1 i3-sensible-terminal.1 i3-dump-log.1 - -%.1: %.man asciidoc.conf - ${A2M} $< +all: + $(MAKE) -C .. mans clean: - for file in $$(echo i3 i3-msg i3-input i3-nagbar i3-wsbar i3-config-wizard i3-migrate-config-to-v4 i3-sensible-editor i3-sensible-pager i3-sensible-terminal i3-dump-log); \ - do \ - rm -f $${file}.1 $${file}.html $${file}.xml; \ - done + $(MAKE) -C .. clean-mans -distclean: clean - rm -f *.1 +.PHONY: all clean diff --git a/i3bar/doc/i3bar.man b/man/i3bar.man similarity index 100% rename from i3bar/doc/i3bar.man rename to man/i3bar.man diff --git a/man/man.mk b/man/man.mk new file mode 100644 index 00000000..f999dc78 --- /dev/null +++ b/man/man.mk @@ -0,0 +1,32 @@ +DISTCLEAN_TARGETS += clean-mans + +A2X = a2x + +A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $< + +MANS_1 = \ + man/i3.1 \ + man/i3bar.1 \ + man/i3-msg.1 \ + man/i3-input.1 \ + man/i3-nagbar.1 \ + man/i3-config-wizard.1 \ + man/i3-migrate-config-to-v4.1 \ + man/i3-sensible-editor.1 \ + man/i3-sensible-pager.1 \ + man/i3-sensible-terminal.1 \ + man/i3-dump-log.1 + +MANS = \ + $(MANS_1) + +mans: $(MANS) + +$(MANS_1): %.1: %.man man/asciidoc.conf + $(A2X_MAN_CALL) + +clean-mans: + for file in $(notdir $(MANS)); \ + do \ + rm -f man/$${file} man/$${file%.*}.html man/$${file%.*}.xml; \ + done From d5558727531b9caf1db79ea21ac958f0f6fa19cb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 22 Jul 2012 21:04:35 +0200 Subject: [PATCH 033/200] Bugfix: include i3-input/i3-input.mk in the dist tarballs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 94c9c63e..ed0291ed 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ dist: distclean find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \; # Only copy source code from i3-input mkdir i3-${VERSION}/i3-input - find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; + find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; sed -e 's/^GIT_VERSION:=\(.*\)/GIT_VERSION:=$(shell /bin/echo '${GIT_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' common.mk > i3-${VERSION}/common.mk # Pre-generate a manpage to allow distributors to skip this step and save some dependencies $(MAKE) mans From 45b3033996ab829026cfb5e0a8fbfe685cb750cf Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 22 Jul 2012 21:07:32 +0200 Subject: [PATCH 034/200] debian: i3bar.1 has been moved to man/ --- debian/i3-wm.manpages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/i3-wm.manpages b/debian/i3-wm.manpages index 4df7233c..a1b05bd3 100644 --- a/debian/i3-wm.manpages +++ b/debian/i3-wm.manpages @@ -8,4 +8,4 @@ man/i3-migrate-config-to-v4.1 man/i3-sensible-pager.1 man/i3-sensible-editor.1 man/i3-sensible-terminal.1 -i3bar/doc/i3bar.1 +man/i3bar.1 From ac7278eb1a9876ebfa03274cda3a4faf92d7958b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 22 Jul 2012 21:37:26 +0200 Subject: [PATCH 035/200] resizing: traverse containers up properly (+test) (Thanks oblique) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In certain situations (when you have a h-split within a h-split) you couldn’t properly resize previously. This commit makes the resize command properly traverse up the containers. fixes #754 --- src/commands.c | 66 +++++++++++++++++++-------------- testcases/t/191-resize-levels.t | 29 +++++++++++++++ 2 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 testcases/t/191-resize-levels.t diff --git a/src/commands.c b/src/commands.c index 3066a809..7b6d55a7 100644 --- a/src/commands.c +++ b/src/commands.c @@ -519,6 +519,8 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int LOG("tiling resize\n"); /* get the appropriate current container (skip stacked/tabbed cons) */ Con *current = focused; + Con *other = NULL; + double percentage = 0; while (current->parent->layout == L_STACKED || current->parent->layout == L_TABBED) current = current->parent; @@ -527,40 +529,50 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int orientation_t search_orientation = (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); - while (current->type != CT_WORKSPACE && - current->type != CT_FLOATING_CON && - current->parent->orientation != search_orientation) - current = current->parent; + do { + if (current->parent->orientation != search_orientation) { + current = current->parent; + continue; + } - /* get the default percentage */ - int children = con_num_children(current->parent); - Con *other; - LOG("ins. %d children\n", children); - double percentage = 1.0 / children; - LOG("default percentage = %f\n", percentage); + /* get the default percentage */ + int children = con_num_children(current->parent); + LOG("ins. %d children\n", children); + percentage = 1.0 / children; + LOG("default percentage = %f\n", percentage); - orientation_t orientation = current->parent->orientation; + orientation_t orientation = current->parent->orientation; - if ((orientation == HORIZ && - (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || - (orientation == VERT && - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { - LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", - (orientation == HORIZ ? "horizontal" : "vertical")); + if ((orientation == HORIZ && + (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || + (orientation == VERT && + (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { + LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", + (orientation == HORIZ ? "horizontal" : "vertical")); + ysuccess(false); + return false; + } + + if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { + other = TAILQ_PREV(current, nodes_head, nodes); + } else { + other = TAILQ_NEXT(current, nodes); + } + if (other == TAILQ_END(workspaces)) { + LOG("No other container in this direction found, trying to look further up in the tree...\n"); + current = current->parent; + continue; + } + break; + } while (current->type != CT_WORKSPACE && + current->type != CT_FLOATING_CON); + + if (other == NULL) { + LOG("No other container in this direction found, trying to look further up in the tree...\n"); ysuccess(false); return false; } - if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { - other = TAILQ_PREV(current, nodes_head, nodes); - } else { - other = TAILQ_NEXT(current, nodes); - } - if (other == TAILQ_END(workspaces)) { - LOG("No other container in this direction found, cannot resize.\n"); - ysuccess(false); - return false; - } LOG("other->percent = %f\n", other->percent); LOG("current->percent before = %f\n", current->percent); if (current->percent == 0.0) diff --git a/testcases/t/191-resize-levels.t b/testcases/t/191-resize-levels.t new file mode 100644 index 00000000..230a4915 --- /dev/null +++ b/testcases/t/191-resize-levels.t @@ -0,0 +1,29 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verifies that you can resize across different levels of containers even when +# they are all of the same orientation. +# (Ticket #754) +use i3test; + +my $tmp = fresh_workspace; + +open_window; +open_window; +cmd 'split v'; +my $middle = open_window; +open_window; +cmd 'focus parent'; +cmd 'split h'; +open_window; + +cmd '[id="' . $middle->id . '"] focus'; +is($x->input_focus, $middle->id, 'middle window focused'); + +cmd 'resize grow left 10px or 25ppt'; + +my ($nodes, $focus) = get_ws_content($tmp); + +ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left container got only 25%'); +ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right container got 75%'); + +done_testing; From 3b1b72ecbbd6031be8aa41283aeeb4d936e5cb44 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 22:04:14 +0200 Subject: [PATCH 036/200] *.mk: Support passing specific CFLAGS/LIBS --- i3-config-wizard/i3-config-wizard.mk | 12 +++++++----- i3-dump-log/i3-dump-log.mk | 6 ++++-- i3-input/i3-input.mk | 6 ++++-- i3-msg/i3-msg.mk | 6 ++++-- i3-nagbar/i3-nagbar.mk | 6 ++++-- i3bar/i3bar.mk | 6 ++++-- libi3/libi3.mk | 6 ++++-- src/i3.mk | 16 +++++++++------- 8 files changed, 40 insertions(+), 24 deletions(-) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index ed41941b..583047e7 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -2,16 +2,18 @@ ALL_TARGETS += i3-config-wizard/i3-config-wizard INSTALL_TARGETS += install-i3-config-wizard CLEAN_TARGETS += clean-i3-config-wizard -i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c -i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) -i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) +i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c +i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) +i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) +i3_config_wizard_CFLAGS = +i3_config_wizard_LIBS = i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(CFLAGS) -c -o $@ $< i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] LEX $<" @@ -23,7 +25,7 @@ i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_ i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS) echo "[i3-config-wizard] Link i3-config-wizard" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_config_wizard_LIBS) $(LIBS) install-i3-config-wizard: i3-config-wizard/i3-config-wizard echo "[i3-config-wizard] Install" diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index a29bced4..80bd86cc 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -4,17 +4,19 @@ CLEAN_TARGETS += clean-i3-dump-log i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) +i3_dump_log_CFLAGS = +i3_dump_log_LIBS = i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) echo "[i3-dump-log] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(CFLAGS) -c -o $@ $< i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) echo "[i3-dump-log] Link i3-dump-log" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_dump_log_LIBS) $(LIBS) install-i3-dump-log: i3-dump-log/i3-dump-log echo "[i3-dump-log] Install" diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 35c74964..e7ce6339 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -4,17 +4,19 @@ CLEAN_TARGETS += clean-i3-input i3_input_SOURCES := $(wildcard i3-input/*.c) i3_input_HEADERS := $(wildcard i3-input/*.h) +i3_input_CFLAGS = +i3_input_LIBS = i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) echo "[i3-input] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_input_CFLAGS) $(CFLAGS) -c -o $@ $< i3-input/i3-input: libi3.a $(i3_input_OBJECTS) echo "[i3-input] Link i3-input" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_input_LIBS) $(LIBS) install-i3-input: i3-input/i3-input echo "[i3-input] Install" diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index 9526dc04..cdd44987 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -4,17 +4,19 @@ CLEAN_TARGETS += clean-i3-msg i3_msg_SOURCES := $(wildcard i3-msg/*.c) i3_msg_HEADERS := $(wildcard i3-msg/*.h) +i3_msg_CFLAGS = +i3_msg_LIBS = i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) echo "[i3-msg] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_msg_CFLAGS) $(CFLAGS) -c -o $@ $< i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) echo "[i3-msg] Link i3-msg" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_msg_LIBS) $(LIBS) install-i3-msg: i3-msg/i3-msg echo "[i3-msg] Install" diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 9677b808..78f77d43 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -4,17 +4,19 @@ CLEAN_TARGETS += clean-i3-nagbar i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) +i3_nagbar_CFLAGS = +i3_nagbar_LIBS = i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) echo "[i3-nagbar] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(CFLAGS) -c -o $@ $< i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) echo "[i3-nagbar] Link i3-nagbar" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_nagbar_LIBS) $(LIBS) install-i3-nagbar: i3-nagbar/i3-nagbar echo "[i3-nagbar] Install" diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 3f16f8a2..776961c9 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,17 +4,19 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) +i3bar_CFLAGS = +i3bar_LIBS = i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) echo "[i3bar] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3bar_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< i3bar/i3bar: libi3.a $(i3bar_OBJECTS) echo "[i3bar] Link i3bar" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3bar_LIBS) $(LIBS) install-i3bar: i3bar/i3bar echo "[i3bar] Install" diff --git a/libi3/libi3.mk b/libi3/libi3.mk index 9c9f3c91..bf7b7643 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -2,17 +2,19 @@ CLEAN_TARGETS += clean-libi3 libi3_SOURCES := $(wildcard libi3/*.c) libi3_HEADERS := $(wildcard libi3/*.h) +libi3_CFLAGS = +libi3_LIBS = libi3_OBJECTS := $(libi3_SOURCES:.c=.o) libi3/%.o: libi3/%.c $(libi3_HEADERS) echo "[libi3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(libi3_CFLAGS) $(CFLAGS) -c -o $@ $< libi3.a: $(libi3_OBJECTS) echo "[libi3] AR libi3.a" - ar rcs $@ $^ + ar rcs $@ $^ $(libi3_LIBS) clean-libi3: echo "[libi3] Clean" diff --git a/src/i3.mk b/src/i3.mk index 026bf4a0..5f05b0cc 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -2,16 +2,18 @@ ALL_TARGETS += i3 INSTALL_TARGETS += install-i3 CLEAN_TARGETS += clean-i3 -i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c -i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) +i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c +i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) -i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) +i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) +i3_CFLAGS = +i3_LIBS = i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) src/%.o: src/%.c $(i3_HEADERS) echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o $@ $< src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS) echo "[i3] LEX $<" @@ -26,8 +28,8 @@ src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS) # and once as an object file for i3. src/commands_parser.o: src/commands_parser.c $(i3_HEADERS) i3-command-parser.stamp echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) + $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec echo "[i3] Generating command parser" @@ -36,7 +38,7 @@ i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec i3: libi3.a $(i3_OBJECTS) echo "[i3] Link i3" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) + $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_LIBS) $(LIBS) install-i3: i3 echo "[i3] Install" From c7029a5e21d7d310e3f5d88ad0ca565e05c39cce Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 22:27:45 +0200 Subject: [PATCH 037/200] common.mk: Introduce I3_*FLAGS CPPFLGES, CFLAGS and LDFLAGS should be user variables We now provide default flags but use I3_*FLAGS flags for our own needed flags Also reoder lib flags a bit --- common.mk | 83 ++++++++++++++++------------ i3-config-wizard/i3-config-wizard.mk | 4 +- i3-dump-log/i3-dump-log.mk | 4 +- i3-input/i3-input.mk | 4 +- i3-msg/i3-msg.mk | 4 +- i3-nagbar/i3-nagbar.mk | 4 +- i3bar/i3bar.mk | 4 +- libi3/libi3.mk | 2 +- src/i3.mk | 8 +-- 9 files changed, 64 insertions(+), 53 deletions(-) diff --git a/common.mk b/common.mk index 941da7d2..b334a504 100644 --- a/common.mk +++ b/common.mk @@ -18,6 +18,34 @@ endif GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f $(TOPDIR)/.git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' $(TOPDIR)/.git/HEAD || echo 'unknown'))" VERSION:=$(shell git describe --tags --abbrev=0) + +## Generic flags + +# Default CFLAGS that users should be able to override +ifeq ($(DEBUG),1) +# Extended debugging flags, macros shall be available in gcc +CFLAGS ?= -pipe -gdwarf-2 -g3 +else +CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition +endif + +# Default LDFLAGS that users should be able to override +LDFLAGS ?= $(as_needed_LDFLAG) + +# Common CFLAGS for all i3 related binaries +I3_CFLAGS = -std=c99 +I3_CFLAGS += -Wall +# unused-function, unused-label, unused-variable are turned on by -Wall +# We don’t want unused-parameter because of the use of many callbacks +I3_CFLAGS += -Wunused-value +I3_CFLAGS += -Iinclude + +I3_CPPFLAGS = -DI3_VERSION=\"${GIT_VERSION}\" +I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" + + +## Libraries flags + ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1) $(error "pkg-config was not found") endif @@ -35,16 +63,9 @@ endif cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null) ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2)) -CFLAGS += -std=c99 -CFLAGS += -pipe -CFLAGS += -Wall -# unused-function, unused-label, unused-variable are turned on by -Wall -# We don’t want unused-parameter because of the use of many callbacks -CFLAGS += -Wunused-value -CFLAGS += -Iinclude CFLAGS += $(call cflags_for_lib, xcb-keysyms) ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) -CPPFLAGS += -DXCB_COMPAT +I3_CPPFLAGS += -DXCB_COMPAT CFLAGS += $(call cflags_for_lib, xcb-atom) CFLAGS += $(call cflags_for_lib, xcb-aux) else @@ -57,21 +78,17 @@ CFLAGS += $(call cflags_for_lib, xcb) CFLAGS += $(call cflags_for_lib, xcursor) CFLAGS += $(call cflags_for_lib, x11) CFLAGS += $(call cflags_for_lib, yajl) +# Fallback for libyajl 1 which did not include yajl_version.h. We need +# YAJL_MAJOR from that file to decide which code path should be used. +CFLAGS += -idirafter $(TOPDIR)/yajl-fallback CFLAGS += $(call cflags_for_lib, libev) CFLAGS += $(call cflags_for_lib, libpcre) -CFLAGS += $(call cflags_for_lib, libstartup-notification-1.0) -CPPFLAGS += -DI3_VERSION=\"${GIT_VERSION}\" -CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" - ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1) -CPPFLAGS += -DPCRE_HAS_UCP=1 +I3_CPPFLAGS += -DPCRE_HAS_UCP=1 endif +CFLAGS += $(call cflags_for_lib, libstartup-notification-1.0) LIBS += -lm -# Darwin (Mac OS X) doesn’t have librt -ifneq ($(UNAME),Darwin) -LIBS += -lrt -endif LIBS += -L $(TOPDIR) -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) @@ -92,22 +109,25 @@ LIBS += $(call ldflags_for_lib, libev,ev) LIBS += $(call ldflags_for_lib, libpcre,pcre) LIBS += $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) + +## Platform-specific flags + # Please test if -Wl,--as-needed works on your platform and send me a patch. # it is known not to work on Darwin (Mac OS X) ifneq (,$(filter Linux GNU GNU/%, $(UNAME))) -LDFLAGS += -Wl,--as-needed +as_needed_LDFLAG = -Wl,--as-needed endif ifeq ($(UNAME),NetBSD) # We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv -CFLAGS += -idirafter /usr/pkg/include -LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib +I3_CFLAGS += -idirafter /usr/pkg/include +I3_LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib endif ifeq ($(UNAME),OpenBSD) -CFLAGS += -I${X11BASE}/include +I3_CFLAGS += -I${X11BASE}/include LIBS += -liconv -LDFLAGS += -L${X11BASE}/lib +I3_LDFLAGS += -L${X11BASE}/lib endif ifeq ($(UNAME),FreeBSD) @@ -116,27 +136,18 @@ endif ifeq ($(UNAME),Darwin) LIBS += -liconv +else +# Darwin (Mac OS X) doesn’t have librt +LIBS += -lrt endif -# Fallback for libyajl 1 which did not include yajl_version.h. We need -# YAJL_MAJOR from that file to decide which code path should be used. -CFLAGS += -idirafter $(TOPDIR)/yajl-fallback - ifneq (,$(filter Linux GNU GNU/%, $(UNAME))) -CPPFLAGS += -D_GNU_SOURCE +I3_CPPFLAGS += -D_GNU_SOURCE endif -ifeq ($(DEBUG),1) -# Extended debugging flags, macros shall be available in gcc -CFLAGS += -gdwarf-2 -CFLAGS += -g3 -else -CFLAGS += -O2 -CFLAGS += -freorder-blocks-and-partition -endif ifeq ($(COVERAGE),1) -CFLAGS += -fprofile-arcs -ftest-coverage +I3_CFLAGS += -fprofile-arcs -ftest-coverage LIBS += -lgcov endif diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 583047e7..fa197be3 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -13,7 +13,7 @@ i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_con i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] CC $<" - $(CC) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] LEX $<" @@ -25,7 +25,7 @@ i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_ i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS) echo "[i3-config-wizard] Link i3-config-wizard" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_config_wizard_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_config_wizard_LIBS) $(LIBS) install-i3-config-wizard: i3-config-wizard/i3-config-wizard echo "[i3-config-wizard] Install" diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index 80bd86cc..edaead4c 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -12,11 +12,11 @@ i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) echo "[i3-dump-log] CC $<" - $(CC) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) echo "[i3-dump-log] Link i3-dump-log" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_dump_log_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_dump_log_LIBS) $(LIBS) install-i3-dump-log: i3-dump-log/i3-dump-log echo "[i3-dump-log] Install" diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index e7ce6339..67a88bf1 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -12,11 +12,11 @@ i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) echo "[i3-input] CC $<" - $(CC) $(CPPFLAGS) $(i3_input_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-input/i3-input: libi3.a $(i3_input_OBJECTS) echo "[i3-input] Link i3-input" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_input_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_input_LIBS) $(LIBS) install-i3-input: i3-input/i3-input echo "[i3-input] Install" diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index cdd44987..b835185c 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -12,11 +12,11 @@ i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) echo "[i3-msg] CC $<" - $(CC) $(CPPFLAGS) $(i3_msg_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) echo "[i3-msg] Link i3-msg" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_msg_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_msg_LIBS) $(LIBS) install-i3-msg: i3-msg/i3-msg echo "[i3-msg] Install" diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 78f77d43..37c91a98 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -12,11 +12,11 @@ i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) echo "[i3-nagbar] CC $<" - $(CC) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) echo "[i3-nagbar] Link i3-nagbar" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_nagbar_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_nagbar_LIBS) $(LIBS) install-i3-nagbar: i3-nagbar/i3-nagbar echo "[i3-nagbar] Install" diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 776961c9..988e598c 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -12,11 +12,11 @@ i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) echo "[i3bar] CC $<" - $(CC) $(CPPFLAGS) $(i3bar_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< i3bar/i3bar: libi3.a $(i3bar_OBJECTS) echo "[i3bar] Link i3bar" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3bar_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3bar_LIBS) $(LIBS) install-i3bar: i3bar/i3bar echo "[i3bar] Install" diff --git a/libi3/libi3.mk b/libi3/libi3.mk index bf7b7643..6f6bf506 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -10,7 +10,7 @@ libi3_OBJECTS := $(libi3_SOURCES:.c=.o) libi3/%.o: libi3/%.c $(libi3_HEADERS) echo "[libi3] CC $<" - $(CC) $(CPPFLAGS) $(libi3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< libi3.a: $(libi3_OBJECTS) echo "[libi3] AR libi3.a" diff --git a/src/i3.mk b/src/i3.mk index 5f05b0cc..ce9f31dd 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -13,7 +13,7 @@ i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) src/%.o: src/%.c $(i3_HEADERS) echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS) echo "[i3] LEX $<" @@ -28,8 +28,8 @@ src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS) # and once as an object file for i3. src/commands_parser.o: src/commands_parser.c $(i3_HEADERS) i3-command-parser.stamp echo "[i3] CC $<" - $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) - $(CC) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec echo "[i3] Generating command parser" @@ -38,7 +38,7 @@ i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec i3: libi3.a $(i3_OBJECTS) echo "[i3] Link i3" - $(CC) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_LIBS) $(LIBS) install-i3: i3 echo "[i3] Install" From e232c06885b64bf62aaf8bc8dd68063f9db6edb7 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:00:33 +0200 Subject: [PATCH 038/200] common.mk: Split libstartup-notification flags --- common.mk | 6 ++++-- src/i3.mk | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common.mk b/common.mk index b334a504..fb4fecaf 100644 --- a/common.mk +++ b/common.mk @@ -86,7 +86,6 @@ CFLAGS += $(call cflags_for_lib, libpcre) ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1) I3_CPPFLAGS += -DPCRE_HAS_UCP=1 endif -CFLAGS += $(call cflags_for_lib, libstartup-notification-1.0) LIBS += -lm LIBS += -L $(TOPDIR) -li3 @@ -107,7 +106,10 @@ LIBS += $(call ldflags_for_lib, x11,X11) LIBS += $(call ldflags_for_lib, yajl,yajl) LIBS += $(call ldflags_for_lib, libev,ev) LIBS += $(call ldflags_for_lib, libpcre,pcre) -LIBS += $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) + +# startup-notification +LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0) +LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) ## Platform-specific flags diff --git a/src/i3.mk b/src/i3.mk index ce9f31dd..dc86898e 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = -i3_LIBS = +i3_CFLAGS = $(LIBSN_CFLAGS) +i3_LIBS = $(LIBSN_LIBS) i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 79fc8462c0d0d3ad4b3efee94d6ce0513d36ecf6 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:07:10 +0200 Subject: [PATCH 039/200] common.mk: Split libpcre flags --- common.mk | 12 +++++++----- src/i3.mk | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/common.mk b/common.mk index fb4fecaf..36d90318 100644 --- a/common.mk +++ b/common.mk @@ -82,10 +82,6 @@ CFLAGS += $(call cflags_for_lib, yajl) # YAJL_MAJOR from that file to decide which code path should be used. CFLAGS += -idirafter $(TOPDIR)/yajl-fallback CFLAGS += $(call cflags_for_lib, libev) -CFLAGS += $(call cflags_for_lib, libpcre) -ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1) -I3_CPPFLAGS += -DPCRE_HAS_UCP=1 -endif LIBS += -lm LIBS += -L $(TOPDIR) -li3 @@ -105,7 +101,13 @@ LIBS += $(call ldflags_for_lib, xcursor,Xcursor) LIBS += $(call ldflags_for_lib, x11,X11) LIBS += $(call ldflags_for_lib, yajl,yajl) LIBS += $(call ldflags_for_lib, libev,ev) -LIBS += $(call ldflags_for_lib, libpcre,pcre) + +# libpcre +PCRE_CFLAGS := $(call cflags_for_lib, libpcre) +ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1) +I3_CPPFLAGS += -DPCRE_HAS_UCP=1 +endif +PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre) # startup-notification LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0) diff --git a/src/i3.mk b/src/i3.mk index dc86898e..390210f1 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(LIBSN_CFLAGS) -i3_LIBS = $(LIBSN_LIBS) +i3_CFLAGS = $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(PCRE_LIBS) $(LIBSN_LIBS) i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 30934ea316b386c0363ae61e47d7faf9faf4dc4c Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:20:29 +0200 Subject: [PATCH 040/200] common.mk: Split libev flags --- common.mk | 6 ++++-- i3bar/i3bar.mk | 4 ++-- src/i3.mk | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/common.mk b/common.mk index 36d90318..81ad2be5 100644 --- a/common.mk +++ b/common.mk @@ -81,7 +81,6 @@ CFLAGS += $(call cflags_for_lib, yajl) # Fallback for libyajl 1 which did not include yajl_version.h. We need # YAJL_MAJOR from that file to decide which code path should be used. CFLAGS += -idirafter $(TOPDIR)/yajl-fallback -CFLAGS += $(call cflags_for_lib, libev) LIBS += -lm LIBS += -L $(TOPDIR) -li3 @@ -100,7 +99,10 @@ LIBS += $(call ldflags_for_lib, xcb,xcb) LIBS += $(call ldflags_for_lib, xcursor,Xcursor) LIBS += $(call ldflags_for_lib, x11,X11) LIBS += $(call ldflags_for_lib, yajl,yajl) -LIBS += $(call ldflags_for_lib, libev,ev) + +#libev +LIBEV_CFLAGS := $(call cflags_for_lib, libev) +LIBEV_LIBS := $(call ldflags_for_lib, libev,ev) # libpcre PCRE_CFLAGS := $(call cflags_for_lib, libpcre) diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 988e598c..e31a1373 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = -i3bar_LIBS = +i3bar_CFLAGS = $(LIBEV_CFLAGS) +i3bar_LIBS = $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 390210f1..16ddae68 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(PCRE_LIBS) $(LIBSN_LIBS) +i3_CFLAGS = $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 85b261c16279cebb67bc76edea41e9ed1f6b76ef Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:21:43 +0200 Subject: [PATCH 041/200] common.mk: Split yajl flags --- common.mk | 12 +++++++----- i3bar/i3bar.mk | 4 ++-- src/i3.mk | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/common.mk b/common.mk index 81ad2be5..a357a7a1 100644 --- a/common.mk +++ b/common.mk @@ -77,10 +77,6 @@ CFLAGS += $(call cflags_for_lib, xcb-randr) CFLAGS += $(call cflags_for_lib, xcb) CFLAGS += $(call cflags_for_lib, xcursor) CFLAGS += $(call cflags_for_lib, x11) -CFLAGS += $(call cflags_for_lib, yajl) -# Fallback for libyajl 1 which did not include yajl_version.h. We need -# YAJL_MAJOR from that file to decide which code path should be used. -CFLAGS += -idirafter $(TOPDIR)/yajl-fallback LIBS += -lm LIBS += -L $(TOPDIR) -li3 @@ -98,7 +94,13 @@ LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) LIBS += $(call ldflags_for_lib, xcb,xcb) LIBS += $(call ldflags_for_lib, xcursor,Xcursor) LIBS += $(call ldflags_for_lib, x11,X11) -LIBS += $(call ldflags_for_lib, yajl,yajl) + +# yajl +YAJL_CFLAGS := $(call cflags_for_lib, yajl) +# Fallback for libyajl 1 which did not include yajl_version.h. We need +# YAJL_MAJOR from that file to decide which code path should be used. +YAJL_CFLAGS += -idirafter $(TOPDIR)/yajl-fallback +YAJL_LIBS := $(call ldflags_for_lib, yajl,yajl) #libev LIBEV_CFLAGS := $(call cflags_for_lib, libev) diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index e31a1373..43b6e478 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(LIBEV_CFLAGS) -i3bar_LIBS = $(LIBEV_LIBS) +i3bar_CFLAGS = $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 16ddae68..e1d781d1 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) +i3_CFLAGS = $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 60bc6b040064484628d253beb0b1abe4e24e403f Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:42:18 +0200 Subject: [PATCH 042/200] common.mk: Split Xcursor flags --- common.mk | 6 ++++-- src/i3.mk | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common.mk b/common.mk index a357a7a1..57034b02 100644 --- a/common.mk +++ b/common.mk @@ -75,7 +75,6 @@ CFLAGS += $(call cflags_for_lib, xcb-icccm) CFLAGS += $(call cflags_for_lib, xcb-xinerama) CFLAGS += $(call cflags_for_lib, xcb-randr) CFLAGS += $(call cflags_for_lib, xcb) -CFLAGS += $(call cflags_for_lib, xcursor) CFLAGS += $(call cflags_for_lib, x11) LIBS += -lm @@ -92,9 +91,12 @@ LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm) LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) LIBS += $(call ldflags_for_lib, xcb,xcb) -LIBS += $(call ldflags_for_lib, xcursor,Xcursor) LIBS += $(call ldflags_for_lib, x11,X11) +# Xcursor +XCURSOR_CFLAGS := $(call cflags_for_lib, xcursor) +XCURSOR_LIBS := $(call ldflags_for_lib, xcursor,Xcursor) + # yajl YAJL_CFLAGS := $(call cflags_for_lib, yajl) # Fallback for libyajl 1 which did not include yajl_version.h. We need diff --git a/src/i3.mk b/src/i3.mk index e1d781d1..363df0b3 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) +i3_CFLAGS = $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 5cf37514827f2bc762555d96536500692cb481ed Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:47:34 +0200 Subject: [PATCH 043/200] common.mk: Move -lm to i3_LIBS --- common.mk | 1 - src/i3.mk | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/common.mk b/common.mk index 57034b02..dda4e35f 100644 --- a/common.mk +++ b/common.mk @@ -77,7 +77,6 @@ CFLAGS += $(call cflags_for_lib, xcb-randr) CFLAGS += $(call cflags_for_lib, xcb) CFLAGS += $(call cflags_for_lib, x11) -LIBS += -lm LIBS += -L $(TOPDIR) -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) diff --git a/src/i3.mk b/src/i3.mk index 363df0b3..6d77d64c 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -7,7 +7,7 @@ i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) i3_CFLAGS = $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) +i3_LIBS = $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From fde8c0dd859b6d2332f454eee7d87bece1e74d6f Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 22 Jul 2012 23:57:39 +0200 Subject: [PATCH 044/200] common.mk: Split Xlib flags --- common.mk | 6 ++++-- i3-config-wizard/i3-config-wizard.mk | 4 ++-- i3bar/i3bar.mk | 4 ++-- src/i3.mk | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common.mk b/common.mk index dda4e35f..da75e5f1 100644 --- a/common.mk +++ b/common.mk @@ -75,7 +75,6 @@ CFLAGS += $(call cflags_for_lib, xcb-icccm) CFLAGS += $(call cflags_for_lib, xcb-xinerama) CFLAGS += $(call cflags_for_lib, xcb-randr) CFLAGS += $(call cflags_for_lib, xcb) -CFLAGS += $(call cflags_for_lib, x11) LIBS += -L $(TOPDIR) -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) @@ -90,7 +89,10 @@ LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm) LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) LIBS += $(call ldflags_for_lib, xcb,xcb) -LIBS += $(call ldflags_for_lib, x11,X11) + +# Xlib +X11_CFLAGS := $(call cflags_for_lib, x11) +X11_LIBS := $(call ldflags_for_lib, x11,X11) # Xcursor XCURSOR_CFLAGS := $(call cflags_for_lib, xcursor) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index fa197be3..fd932542 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = -i3_config_wizard_LIBS = +i3_config_wizard_CFLAGS = $(X11_CFLAGS) +i3_config_wizard_LIBS = $(X11_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 43b6e478..9ede97f6 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(YAJL_LIBS) $(LIBEV_LIBS) +i3bar_CFLAGS = $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 6d77d64c..b8557eb3 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +i3_CFLAGS = $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 51a0f312f296b8f085759c027f74dea3d242a279 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Mon, 23 Jul 2012 00:02:01 +0200 Subject: [PATCH 045/200] common.mk: Split WM XCB flags --- common.mk | 14 ++++++++------ src/i3.mk | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common.mk b/common.mk index da75e5f1..e347574e 100644 --- a/common.mk +++ b/common.mk @@ -71,9 +71,6 @@ CFLAGS += $(call cflags_for_lib, xcb-aux) else CFLAGS += $(call cflags_for_lib, xcb-util) endif -CFLAGS += $(call cflags_for_lib, xcb-icccm) -CFLAGS += $(call cflags_for_lib, xcb-xinerama) -CFLAGS += $(call cflags_for_lib, xcb-randr) CFLAGS += $(call cflags_for_lib, xcb) LIBS += -L $(TOPDIR) -li3 @@ -85,11 +82,16 @@ LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) else LIBS += $(call ldflags_for_lib, xcb-util) endif -LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm) -LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) -LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) LIBS += $(call ldflags_for_lib, xcb,xcb) +# XCB WM stuff +XCB_WM_CFLAGS := $(call cflags_for_lib, xcb-icccm) +XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-xinerama) +XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-randr) +XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm) +XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) +XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) + # Xlib X11_CFLAGS := $(call cflags_for_lib, x11) X11_LIBS := $(call ldflags_for_lib, x11,X11) diff --git a/src/i3.mk b/src/i3.mk index b8557eb3..e056342d 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +i3_CFLAGS = $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 5e0cd52f10e0ac6bdd140bc356e39d6ce521fdef Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Mon, 23 Jul 2012 00:06:37 +0200 Subject: [PATCH 046/200] common.mk: Split XCB keyboard flags --- common.mk | 6 ++++-- i3-config-wizard/i3-config-wizard.mk | 4 ++-- i3-input/i3-input.mk | 4 ++-- src/i3.mk | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common.mk b/common.mk index e347574e..3b75d380 100644 --- a/common.mk +++ b/common.mk @@ -63,7 +63,6 @@ endif cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null) ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2)) -CFLAGS += $(call cflags_for_lib, xcb-keysyms) ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) I3_CPPFLAGS += -DXCB_COMPAT CFLAGS += $(call cflags_for_lib, xcb-atom) @@ -75,7 +74,6 @@ CFLAGS += $(call cflags_for_lib, xcb) LIBS += -L $(TOPDIR) -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) -LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom) LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) @@ -84,6 +82,10 @@ LIBS += $(call ldflags_for_lib, xcb-util) endif LIBS += $(call ldflags_for_lib, xcb,xcb) +# XCB keyboard stuff +XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms) +XCB_KBD_LIBS := $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) + # XCB WM stuff XCB_WM_CFLAGS := $(call cflags_for_lib, xcb-icccm) XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-xinerama) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index fd932542..1d2bc37a 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(X11_CFLAGS) -i3_config_wizard_LIBS = $(X11_LIBS) +i3_config_wizard_CFLAGS = $(XCB_KBD_CFLAGS) $(X11_CFLAGS) +i3_config_wizard_LIBS = $(XCB_KBD_LIBS) $(X11_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 67a88bf1..849fbc69 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-input i3_input_SOURCES := $(wildcard i3-input/*.c) i3_input_HEADERS := $(wildcard i3-input/*.h) -i3_input_CFLAGS = -i3_input_LIBS = +i3_input_CFLAGS = $(XCB_KBD_CFLAGS) +i3_input_LIBS = $(XCB_KBD_LIBS) i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index e056342d..49b7ce46 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +i3_CFLAGS = $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From 0b4ee7a1da6f1c76b71a690fd0f2300e212d6a95 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Mon, 23 Jul 2012 00:10:42 +0200 Subject: [PATCH 047/200] common.mk: Split XCB common flags --- common.mk | 25 ++++++++++++------------- i3-config-wizard/i3-config-wizard.mk | 4 ++-- i3-dump-log/i3-dump-log.mk | 4 ++-- i3-input/i3-input.mk | 4 ++-- i3-msg/i3-msg.mk | 4 ++-- i3-nagbar/i3-nagbar.mk | 4 ++-- i3bar/i3bar.mk | 4 ++-- src/i3.mk | 4 ++-- 8 files changed, 26 insertions(+), 27 deletions(-) diff --git a/common.mk b/common.mk index 3b75d380..d6a7572f 100644 --- a/common.mk +++ b/common.mk @@ -63,24 +63,23 @@ endif cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null) ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2)) -ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) -I3_CPPFLAGS += -DXCB_COMPAT -CFLAGS += $(call cflags_for_lib, xcb-atom) -CFLAGS += $(call cflags_for_lib, xcb-aux) -else -CFLAGS += $(call cflags_for_lib, xcb-util) -endif -CFLAGS += $(call cflags_for_lib, xcb) LIBS += -L $(TOPDIR) -li3 -LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) + +# XCB common stuff +XCB_CFLAGS := $(call cflags_for_lib, xcb) +XCB_CFLAGS += $(call cflags_for_lib, xcb-event) +XCB_LIBS := $(call ldflags_for_lib, xcb,xcb) +XCB_LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1) -LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom) -LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) +XCB_CFLAGS += $(call cflags_for_lib, xcb-atom) +XCB_CFLAGS += $(call cflags_for_lib, xcb-aux) +XCB_LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom) +XCB_LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) else -LIBS += $(call ldflags_for_lib, xcb-util) +XCB_CFLAGS += $(call cflags_for_lib, xcb-util) +XCB_LIBS += $(call ldflags_for_lib, xcb-util) endif -LIBS += $(call ldflags_for_lib, xcb,xcb) # XCB keyboard stuff XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 1d2bc37a..bce679df 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(XCB_KBD_CFLAGS) $(X11_CFLAGS) -i3_config_wizard_LIBS = $(XCB_KBD_LIBS) $(X11_LIBS) +i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) +i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index edaead4c..1508a8dc 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-dump-log i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) -i3_dump_log_CFLAGS = -i3_dump_log_LIBS = +i3_dump_log_CFLAGS = $(XCB_CFLAGS) +i3_dump_log_LIBS = $(XCB_LIBS) i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 849fbc69..9fb4b194 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-input i3_input_SOURCES := $(wildcard i3-input/*.c) i3_input_HEADERS := $(wildcard i3-input/*.h) -i3_input_CFLAGS = $(XCB_KBD_CFLAGS) -i3_input_LIBS = $(XCB_KBD_LIBS) +i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) +i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index b835185c..bab26da6 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-msg i3_msg_SOURCES := $(wildcard i3-msg/*.c) i3_msg_HEADERS := $(wildcard i3-msg/*.h) -i3_msg_CFLAGS = -i3_msg_LIBS = +i3_msg_CFLAGS = $(XCB_CFLAGS) +i3_msg_LIBS = $(XCB_LIBS) i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 37c91a98..f54d8380 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) -i3_nagbar_CFLAGS = -i3_nagbar_LIBS = +i3_nagbar_CFLAGS = $(XCB_CFLAGS) +i3_nagbar_LIBS = $(XCB_LIBS) i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 9ede97f6..cf3bf346 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) +i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 49b7ce46..6030bbc6 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) From eb66337020b221ccf2f388c63cc4378c85897fa4 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Mon, 23 Jul 2012 00:13:16 +0200 Subject: [PATCH 048/200] common.mk: Little reordering --- common.mk | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common.mk b/common.mk index d6a7572f..75f6f269 100644 --- a/common.mk +++ b/common.mk @@ -63,9 +63,6 @@ endif cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null) ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2)) - -LIBS += -L $(TOPDIR) -li3 - # XCB common stuff XCB_CFLAGS := $(call cflags_for_lib, xcb) XCB_CFLAGS += $(call cflags_for_lib, xcb-event) @@ -123,6 +120,8 @@ PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre) LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0) LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) +# libi3 +LIBS = -L$(TOPDIR) -li3 ## Platform-specific flags From 0f22b105c1e76756a41d0275ddb552e9ad8c7f6e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 23 Jul 2012 10:56:44 +0200 Subject: [PATCH 049/200] Fix linking by linking libi3 first and its dependencies afterwards Interestingly, compilation was only broken on some systems apparently --- i3-dump-log/i3-dump-log.mk | 2 +- i3-msg/i3-msg.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index 1508a8dc..17e53a6d 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -16,7 +16,7 @@ i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) echo "[i3-dump-log] Link i3-dump-log" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_dump_log_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_dump_log_LIBS) install-i3-dump-log: i3-dump-log/i3-dump-log echo "[i3-dump-log] Install" diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index bab26da6..01d5fc7e 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -16,7 +16,7 @@ i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) echo "[i3-msg] Link i3-msg" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_msg_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_msg_LIBS) install-i3-msg: i3-msg/i3-msg echo "[i3-msg] Install" From b8d77eb59ed70d6354feb24717b6e7538978a0d0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 23 Jul 2012 11:01:52 +0200 Subject: [PATCH 050/200] docs/ipc: document the 'window' field (Thanks jh) fixes #758 --- docs/ipc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/ipc b/docs/ipc index 83ab7218..54e7fa1a 100644 --- a/docs/ipc +++ b/docs/ipc @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -February 2012 +July 2012 This document describes how to interface with i3 from a separate process. This is useful for example to remote-control i3 (to write test cases for example) or @@ -296,6 +296,11 @@ window_rect (map):: geometry (map):: The original geometry the window specified when i3 mapped it. Used when switching a window to floating mode, for example. +window (integer):: + The X11 window ID of the *actual client window* inside this container. + This field is set to null for split containers or otherwise empty + 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. focused (bool):: From ea31cde43b180422cfedd60328496c1df0759d38 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 23 Jul 2012 11:03:16 +0200 Subject: [PATCH 051/200] docs/ipc: update links to libraries --- docs/ipc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ipc b/docs/ipc index 54e7fa1a..90718405 100644 --- a/docs/ipc +++ b/docs/ipc @@ -626,7 +626,7 @@ C:: Ruby:: http://github.com/badboy/i3-ipc Perl:: - http://search.cpan.org/search?query=AnyEvent::I3 + https://metacpan.org/module/AnyEvent::I3 Python:: - https://github.com/whitelynx/i3ipc - https://github.com/ziberna/i3-py (includes higher-level features) + * https://github.com/whitelynx/i3ipc + * https://github.com/ziberna/i3-py (includes higher-level features) From 395a6aaee59d5a9f7539c8f934b923bc206a4202 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Mon, 30 Jul 2012 17:24:31 +0200 Subject: [PATCH 052/200] common.mk: Rework version usage --- Makefile | 4 ++-- common.mk | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index ed0291ed..54d843f3 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ dist: distclean [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION} [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2 mkdir i3-${VERSION} - cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen Makefile i3-${VERSION} + cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION} cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs i3-${VERSION} # Only copy toplevel documentation (important stuff) mkdir i3-${VERSION}/docs @@ -42,7 +42,7 @@ dist: distclean # Only copy source code from i3-input mkdir i3-${VERSION}/i3-input find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; - sed -e 's/^GIT_VERSION:=\(.*\)/GIT_VERSION:=$(shell /bin/echo '${GIT_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' common.mk > i3-${VERSION}/common.mk + echo -n '${VERSION} ($(shell git log --pretty=format:%cd --date=short -n1))' > i3-${VERSION}/VERSION # Pre-generate a manpage to allow distributors to skip this step and save some dependencies $(MAKE) mans cp man/*.1 i3-${VERSION}/man/ diff --git a/common.mk b/common.mk index 75f6f269..f202166d 100644 --- a/common.mk +++ b/common.mk @@ -14,9 +14,14 @@ ifndef SYSCONFDIR SYSCONFDIR=$(PREFIX)/etc endif endif -# The escaping is absurd, but we need to escape for shell, sed, make, define -GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f $(TOPDIR)/.git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' $(TOPDIR)/.git/HEAD || echo 'unknown'))" -VERSION:=$(shell git describe --tags --abbrev=0) + +I3_VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)' +ifeq ('',$(I3_VERSION)) +VERSION := $(shell git describe --tags --abbrev=0) +I3_VERSION := '$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch \"$(shell git describe --tags --always --all | sed s:heads/::)\")' +else +VERSION := ${I3_VERSION} +endif ## Generic flags @@ -40,7 +45,7 @@ I3_CFLAGS += -Wall I3_CFLAGS += -Wunused-value I3_CFLAGS += -Iinclude -I3_CPPFLAGS = -DI3_VERSION=\"${GIT_VERSION}\" +I3_CPPFLAGS = -DI3_VERSION=\"${I3_VERSION}\" I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" From 875130e7e8e32ffbc3166388cfc81893ff6584d1 Mon Sep 17 00:00:00 2001 From: darkraven Date: Fri, 27 Jul 2012 02:53:32 +0800 Subject: [PATCH 053/200] Automatically hide i3bar when it's unneeded. When a workspace marked 'urgent', i3bar unhide itself. if I want to hide it again, I must press the modifier.This sometimes annoys me. In this patch I change the above behavior to this: If a urgent workspace occurs, i3bar will unhide itself; and when you navigates away from the last urgent workspace and there is no more urgent workspace, i3bar will hide itself. --- i3bar/src/xcb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 289d7d9e..419a4723 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -1464,6 +1464,9 @@ void draw_bars() { } i3_ws *ws_walk; + static char *last_urgent_ws = NULL; + bool has_urgent = false, walks_away = true; + TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { DLOG("Drawing Button for WS %s at x = %d, len = %d\n", ws_walk->name, i, ws_walk->name_width); uint32_t fg_color = colors.inactive_ws_fg; @@ -1478,6 +1481,8 @@ void draw_bars() { fg_color = colors.focus_ws_fg; bg_color = colors.focus_ws_bg; border_color = colors.focus_ws_border; + if (last_urgent_ws && strcmp(ws_walk->name, last_urgent_ws) == 0) + walks_away = false; } } if (ws_walk->urgent) { @@ -1485,6 +1490,11 @@ void draw_bars() { fg_color = colors.urgent_ws_fg; bg_color = colors.urgent_ws_bg; border_color = colors.urgent_ws_border; + has_urgent = true; + if (!ws_walk->focused) { + FREE(last_urgent_ws); + last_urgent_ws = sstrdup(ws_walk->name); + } /* The urgent-hint should get noticed, so we unhide the bars shortly */ unhide_bars(); } @@ -1517,6 +1527,11 @@ void draw_bars() { i += 10 + ws_walk->name_width + 1; } + if (!has_urgent && !mod_pressed && walks_away) { + FREE(last_urgent_ws); + hide_bars(); + } + i = 0; } From c64047157d19e51c35ab66d13a4bb86aafaf3217 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Aug 2012 02:14:56 +0200 Subject: [PATCH 054/200] =?UTF-8?q?config-wizard:=20use=20the=20level=200?= =?UTF-8?q?=20keysym=20whenever=20it=E2=80=99s=20unambiguous?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the code: Try to use the keysym on the first level (lower-case). In case this doesn’t make it ambiguous (think of a keyboard layout having '1' on two different keys, but '!' only on keycode 10), we’ll stick with the keysym of the first level. This reduces a lot of confusion for users who switch keyboard layouts from qwerty to qwertz or other slight variations of qwerty (yes, that happens quite often). --- i3-config-wizard/cfgparse.y | 46 ++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/i3-config-wizard/cfgparse.y b/i3-config-wizard/cfgparse.y index e5a86868..17c3953c 100644 --- a/i3-config-wizard/cfgparse.y +++ b/i3-config-wizard/cfgparse.y @@ -39,6 +39,8 @@ extern FILE *yyin; YY_BUFFER_STATE yy_scan_string(const char *); static struct context *context; +static xcb_connection_t *conn; +static xcb_key_symbols_t *keysyms; /* We don’t need yydebug for now, as we got decent error messages using * yyerror(). Should you ever want to extend the parser, it might be handy @@ -67,6 +69,13 @@ int yywrap() { char *rewrite_binding(const char *bindingline) { char *result = NULL; + conn = xcb_connect(NULL, NULL); + if (conn == NULL || xcb_connection_has_error(conn)) { + fprintf(stderr, "Cannot open display\n"); + exit(1); + } + keysyms = xcb_key_symbols_alloc(conn); + context = calloc(sizeof(struct context), 1); yy_scan_string(bindingline); @@ -81,6 +90,8 @@ char *rewrite_binding(const char *bindingline) { if (context->line_copy) free(context->line_copy); free(context); + xcb_key_symbols_free(keysyms); + xcb_disconnect(conn); return result; } @@ -101,6 +112,28 @@ static char *modifier_to_string(int modifiers) { else return strdup(""); } +/* + * Returns true if sym is bound to any key except for 'except_keycode' on the + * first four layers (normal, shift, mode_switch, mode_switch + shift). + * + */ +static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) { + xcb_keycode_t i, + min_keycode = xcb_get_setup(conn)->min_keycode, + max_keycode = xcb_get_setup(conn)->max_keycode; + + for (i = min_keycode; i && i <= max_keycode; i++) { + if (i == except_keycode) + continue; + for (int level = 0; level < 4; level++) { + if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym) + continue; + return true; + } + } + return false; +} + %} %error-verbose @@ -139,11 +172,22 @@ bindcode: * different key than the upper-case one (unlikely for letters, but * more likely for special characters). */ level = 1; + + /* Try to use the keysym on the first level (lower-case). In case + * this doesn’t make it ambiguous (think of a keyboard layout + * having '1' on two different keys, but '!' only on keycode 10), + * we’ll stick with the keysym of the first level. + * + * This reduces a lot of confusion for users who switch keyboard + * layouts from qwerty to qwertz or other slight variations of + * qwerty (yes, that happens quite often). */ + KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, 0); + if (!keysym_used_on_other_key(sym, $4)) + level = 0; } KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level); char *str = XKeysymToString(sym); char *modifiers = modifier_to_string($3); - // TODO: modifier to string sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $6); free(modifiers); } From 9f05354c1f24174cbf8f7f39cc37cc8fc77cdcea Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Aug 2012 15:17:10 +0200 Subject: [PATCH 055/200] cfgparse: Write custom scripts for i3-sensible-terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This workaround is necessary for terminal emulators which parse -e in a different way: some accept a list of arguments (-e command arg1 arg2 …), some accept only one argument (-e "command arg1 arg2 …"). Therefore, we just create a script and pass that as the one and only argument. --- src/cfgparse.y | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/cfgparse.y b/src/cfgparse.y index ab8be57c..68a51996 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -19,6 +19,9 @@ static Barconfig current_bar; * store this in a separate variable because in the i3 config struct we just * store the i3Font. */ static char *font_pattern; +/* The path to the temporary script files used by i3-nagbar. We need to keep + * them around to delete the files in the i3-nagbar SIGCHLD handler. */ +static char *edit_script_path, *pager_script_path; typedef struct yy_buffer_state *YY_BUFFER_STATE; extern int yylex(struct context *context); @@ -233,6 +236,12 @@ static char *migrate_config(char *input, off_t size) { */ static void nagbar_exited(EV_P_ ev_child *watcher, int revents) { ev_child_stop(EV_A_ watcher); + + if (unlink(edit_script_path) != 0) + warn("Could not delete temporary i3-nagbar script %s", edit_script_path); + if (unlink(pager_script_path) != 0) + warn("Could not delete temporary i3-nagbar script %s", pager_script_path); + if (!WIFEXITED(watcher->rstatus)) { fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n"); return; @@ -264,6 +273,23 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) { } #endif +/* + * Writes the given command as a shell script to path. + * Returns true unless something went wrong. + * + */ +static bool write_nagbar_script(const char *path, const char *command) { + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR); + if (fd == -1) { + warn("Could not create temporary script to store the nagbar command"); + return false; + } + write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n")); + write(fd, command, strlen(command)); + close(fd); + return true; +} + /* * Starts an i3-nagbar process which alerts the user that his configuration * file contains one or more errors. Also offers two buttons: One to launch an @@ -276,6 +302,18 @@ static void start_configerror_nagbar(const char *config_path) { return; fprintf(stderr, "Starting i3-nagbar due to configuration errors\n"); + + /* We need to create a custom script containing our actual command + * since not every terminal emulator which is contained in + * i3-sensible-terminal supports -e with multiple arguments (and not + * all of them support -e with one quoted argument either). + * + * NB: The paths need to be unique, that is, don’t assume users close + * their nagbars at any point in time (and they still need to work). + * */ + edit_script_path = get_process_filename("nagbar-cfgerror-edit"); + pager_script_path = get_process_filename("nagbar-cfgerror-pager"); + configerror_pid = fork(); if (configerror_pid == -1) { warn("Could not fork()"); @@ -284,10 +322,17 @@ static void start_configerror_nagbar(const char *config_path) { /* child */ if (configerror_pid == 0) { + char *edit_command, *pager_command; + sasprintf(&edit_command, "i3-sensible-editor \"%s\" && i3-msg reload\n", config_path); + sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename); + if (!write_nagbar_script(edit_script_path, edit_command) || + !write_nagbar_script(pager_script_path, pager_command)) + return; + char *editaction, *pageraction; - sasprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path); - sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename); + sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path); + sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path); char *argv[] = { NULL, /* will be replaced by the executable path */ "-t", From 4cba4c89ad4d61709e764df6824317a2a771412a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Aug 2012 15:20:17 +0200 Subject: [PATCH 056/200] Add xfce4-terminal to i3-sensible-terminal Fixes #753 --- i3-sensible-terminal | 2 +- man/i3-sensible-terminal.man | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/i3-sensible-terminal b/i3-sensible-terminal index a9975740..fddefae1 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -8,7 +8,7 @@ # Distributions/packagers should enhance this script with a # distribution-specific mechanism to find the preferred terminal emulator. On # Debian, there is the x-terminal-emulator symlink for example. -for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do +for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do if which $terminal > /dev/null 2>&1; then exec $terminal "$@" fi diff --git a/man/i3-sensible-terminal.man b/man/i3-sensible-terminal.man index 7e32aab4..1d9f9ff8 100644 --- a/man/i3-sensible-terminal.man +++ b/man/i3-sensible-terminal.man @@ -1,7 +1,7 @@ i3-sensible-terminal(1) ======================= -Michael Stapelberg -v4.1, November 2011 +Michael Stapelberg +v4.2, August 2012 == NAME @@ -30,6 +30,7 @@ It tries to start one of the following (in that order): * xterm * gnome-terminal * roxterm +* xfce4-terminal Please don’t complain about the order: If the user has any preference, he will have $TERMINAL set or modified his i3 configuration file. From 9191b569242c96e12762951fe049d40a03342bcd Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Aug 2012 16:17:24 +0200 Subject: [PATCH 057/200] config: require confirmation when exiting i3 (Thanks Felicitus) Fixes #751 --- i3.config | 2 +- i3.config.keycodes | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/i3.config b/i3.config index 1a457fca..3225b973 100644 --- a/i3.config +++ b/i3.config @@ -103,7 +103,7 @@ bindsym Mod1+Shift+c reload # restart i3 inplace (preserves your layout/session, can be used to upgrade i3) bindsym Mod1+Shift+r restart # exit i3 (logs you out of your X session) -bindsym Mod1+Shift+e exit +bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" # resize window (you can also use the mouse for that) mode "resize" { diff --git a/i3.config.keycodes b/i3.config.keycodes index e360fff9..2f014183 100644 --- a/i3.config.keycodes +++ b/i3.config.keycodes @@ -104,7 +104,7 @@ bindcode $mod+Shift+54 reload # restart i3 inplace (preserves your layout/session, can be used to upgrade i3) bindcode $mod+Shift+27 restart # exit i3 (logs you out of your X session) -bindcode $mod+Shift+26 exit +bindcode $mod+Shift+26 exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" # resize window (you can also use the mouse for that) mode "resize" { From cc7f16007a21a3e105d6b093be615a574e0d486c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Aug 2012 17:43:00 +0200 Subject: [PATCH 058/200] Display i3-nagbar when commands lead to an error e.g. pressing Mod1+x when having the following in your configfile: bindsym Mod1+x some invalid command will lead to an i3-nagbar instance popping up, offering you to view the error log (which will contain parser errors from this commit on). --- generate-command-parser.pl | 2 +- include/all.h | 1 + include/key_press.h | 32 ++++ src/commands.c | 1 + src/commands_parser.c | 16 +- src/handlers.c | 52 ----- src/key_press.c | 303 ++++++++++++++++++++++++++++++ src/util.c | 1 + testcases/t/187-commands-parser.t | 14 +- 9 files changed, 358 insertions(+), 64 deletions(-) create mode 100644 include/key_press.h create mode 100644 src/key_press.c diff --git a/generate-command-parser.pl b/generate-command-parser.pl index 993c64fc..01cbe462 100755 --- a/generate-command-parser.pl +++ b/generate-command-parser.pl @@ -152,7 +152,7 @@ for my $state (@keys) { $cmd =~ s/[^(]+\(//; $cmd =~ s/\)$//; $cmd = ", $cmd" if length($cmd) > 0; - say $callfh qq| printf("$fmt\\n"$cmd);|; + say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|; say $callfh '#endif'; say $callfh " state = $next_state;"; say $callfh " break;"; diff --git a/include/all.h b/include/all.h index 11eaaba4..c6b0351f 100644 --- a/include/all.h +++ b/include/all.h @@ -54,6 +54,7 @@ #include "i3.h" #include "x.h" #include "click.h" +#include "key_press.h" #include "floating.h" #include "config.h" #include "handlers.h" diff --git a/include/key_press.h b/include/key_press.h new file mode 100644 index 00000000..4d469bab --- /dev/null +++ b/include/key_press.h @@ -0,0 +1,32 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * + * key_press.c: key press handler + * + */ +#ifndef _KEY_PRESS_H +#define _KEY_PRESS_H + +/** + * There was a key press. We compare this key code with our bindings table and pass + * the bound action to parse_command(). + * + */ +void handle_key_press(xcb_key_press_event_t *event); + +/** + * Kills the commanderror i3-nagbar process, if any. + * + * Called when reloading/restarting, since the user probably fixed his wrong + * keybindings. + * + * If wait_for_it is set (restarting), this function will waitpid(), otherwise, + * ev is assumed to handle it (reloading). + * + */ +void kill_commanderror_nagbar(bool wait_for_it); + +#endif diff --git a/src/commands.c b/src/commands.c index 7b6d55a7..c0681d26 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1443,6 +1443,7 @@ void cmd_exit(I3_CMD) { void cmd_reload(I3_CMD) { LOG("reloading\n"); kill_configerror_nagbar(false); + kill_commanderror_nagbar(false); load_configuration(conn, NULL, true); x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ diff --git a/src/commands_parser.c b/src/commands_parser.c index e505c944..9af8e981 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -389,9 +389,9 @@ struct CommandResult *parse_command(const char *input) { position[(copywalk - input)] = (copywalk >= walk ? '^' : ' '); position[len] = '\0'; - printf("%s\n", errormessage); - printf("Your command: %s\n", input); - printf(" %s\n", position); + ELOG("%s\n", errormessage); + ELOG("Your command: %s\n", input); + ELOG(" %s\n", position); /* Format this error message as a JSON reply. */ y(map_open); @@ -435,7 +435,15 @@ void debuglog(char *fmt, ...) { va_list args; va_start(args, fmt); - fprintf(stderr, "# "); + fprintf(stdout, "# "); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void errorlog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } diff --git a/src/handlers.c b/src/handlers.c index cb76eb78..048d3a3a 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -77,58 +77,6 @@ bool event_is_ignored(const int sequence, const int response_type) { return false; } - -/* - * There was a key press. We compare this key code with our bindings table and pass - * the bound action to parse_command(). - * - */ -static void handle_key_press(xcb_key_press_event_t *event) { - - last_timestamp = event->time; - - DLOG("Keypress %d, state raw = %d\n", event->detail, event->state); - - /* Remove the numlock bit, all other bits are modifiers we can bind to */ - uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); - DLOG("(removed numlock, state = %d)\n", state_filtered); - /* Only use the lower 8 bits of the state (modifier masks) so that mouse - * button masks are filtered out */ - state_filtered &= 0xFF; - DLOG("(removed upper 8 bits, state = %d)\n", state_filtered); - - if (xkb_current_group == XkbGroup2Index) - state_filtered |= BIND_MODE_SWITCH; - - DLOG("(checked mode_switch, state %d)\n", state_filtered); - - /* Find the binding */ - Binding *bind = get_binding(state_filtered, event->detail); - - /* No match? Then the user has Mode_switch enabled but does not have a - * specific keybinding. Fall back to the default keybindings (without - * Mode_switch). Makes it much more convenient for users of a hybrid - * layout (like us, ru). */ - if (bind == NULL) { - state_filtered &= ~(BIND_MODE_SWITCH); - DLOG("no match, new state_filtered = %d\n", state_filtered); - if ((bind = get_binding(state_filtered, event->detail)) == NULL) { - ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n", - state_filtered, event->detail); - return; - } - } - - char *command_copy = sstrdup(bind->command); - struct CommandResult *command_output = parse_command(command_copy); - free(command_copy); - - if (command_output->needs_tree_render) - tree_render(); - - yajl_gen_free(command_output->json_gen); -} - /* * Called with coordinates of an enter_notify event or motion_notify event * to check if the user crossed virtual screen boundaries and adjust the diff --git a/src/key_press.c b/src/key_press.c new file mode 100644 index 00000000..9aaea8f9 --- /dev/null +++ b/src/key_press.c @@ -0,0 +1,303 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * + * key_press.c: key press handler + * + */ +#include +#include +#include +#include +#include "all.h" + +static int current_nesting_level; +static bool success_key; +static bool command_failed; + +/* XXX: I don’t want to touch too much of the nagbar code at once, but we + * should refactor this with src/cfgparse.y into a clean generic nagbar + * interface. It might come in handy in other situations within i3, too. */ +static char *pager_script_path; +static pid_t nagbar_pid = -1; + +/* + * Handler which will be called when we get a SIGCHLD for the nagbar, meaning + * it exited (or could not be started, depending on the exit code). + * + */ +static void nagbar_exited(EV_P_ ev_child *watcher, int revents) { + ev_child_stop(EV_A_ watcher); + + if (unlink(pager_script_path) != 0) + warn("Could not delete temporary i3-nagbar script %s", pager_script_path); + + if (!WIFEXITED(watcher->rstatus)) { + fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n"); + return; + } + + int exitcode = WEXITSTATUS(watcher->rstatus); + printf("i3-nagbar process exited with status %d\n", exitcode); + if (exitcode == 2) { + fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n"); + } + + nagbar_pid = -1; +} + +/* We need ev >= 4 for the following code. Since it is not *that* important (it + * only makes sure that there are no i3-nagbar instances left behind) we still + * support old systems with libev 3. */ +#if EV_VERSION_MAJOR >= 4 +/* + * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal + * SIGKILL (9) to make sure there are no left-over i3-nagbar processes. + * + */ +static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) { + if (nagbar_pid != -1) { + LOG("Sending SIGKILL (%d) to i3-nagbar with PID %d\n", SIGKILL, nagbar_pid); + kill(nagbar_pid, SIGKILL); + } +} +#endif + +/* + * Writes the given command as a shell script to path. + * Returns true unless something went wrong. + * + */ +static bool write_nagbar_script(const char *path, const char *command) { + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR); + if (fd == -1) { + warn("Could not create temporary script to store the nagbar command"); + return false; + } + write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n")); + write(fd, command, strlen(command)); + close(fd); + return true; +} + +/* + * Starts an i3-nagbar process which alerts the user that his configuration + * file contains one or more errors. Also offers two buttons: One to launch an + * $EDITOR on the config file and another one to launch a $PAGER on the error + * logfile. + * + */ +static void start_commanderror_nagbar(void) { + if (nagbar_pid != -1) { + DLOG("i3-nagbar for command error already running, not starting again.\n"); + return; + } + + DLOG("Starting i3-nagbar due to command error\n"); + + /* We need to create a custom script containing our actual command + * since not every terminal emulator which is contained in + * i3-sensible-terminal supports -e with multiple arguments (and not + * all of them support -e with one quoted argument either). + * + * NB: The paths need to be unique, that is, don’t assume users close + * their nagbars at any point in time (and they still need to work). + * */ + pager_script_path = get_process_filename("nagbar-cfgerror-pager"); + + nagbar_pid = fork(); + if (nagbar_pid == -1) { + warn("Could not fork()"); + return; + } + + /* child */ + if (nagbar_pid == 0) { + char *pager_command; + sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename); + if (!write_nagbar_script(pager_script_path, pager_command)) + return; + + char *pageraction; + sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path); + char *argv[] = { + NULL, /* will be replaced by the executable path */ + "-t", + "error", + "-m", + "The configured command for this shortcut could not be run successfully.", + "-b", + "show errors", + pageraction, + NULL + }; + exec_i3_utility("i3-nagbar", argv); + } + + /* parent */ + /* install a child watcher */ + ev_child *child = smalloc(sizeof(ev_child)); + ev_child_init(child, &nagbar_exited, nagbar_pid, 0); + ev_child_start(main_loop, child); + +/* We need ev >= 4 for the following code. Since it is not *that* important (it + * only makes sure that there are no i3-nagbar instances left behind) we still + * support old systems with libev 3. */ +#if EV_VERSION_MAJOR >= 4 + /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is + * still running) */ + ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup)); + ev_cleanup_init(cleanup, nagbar_cleanup); + ev_cleanup_start(main_loop, cleanup); +#endif +} + +/* + * Kills the commanderror i3-nagbar process, if any. + * + * Called when reloading/restarting, since the user probably fixed his wrong + * keybindings. + * + * If wait_for_it is set (restarting), this function will waitpid(), otherwise, + * ev is assumed to handle it (reloading). + * + */ +void kill_commanderror_nagbar(bool wait_for_it) { + if (nagbar_pid == -1) + return; + + if (kill(nagbar_pid, SIGTERM) == -1) + warn("kill(configerror_nagbar) failed"); + + if (!wait_for_it) + return; + + /* When restarting, we don’t enter the ev main loop anymore and after the + * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD + * for us and we would end up with a process. Therefore we + * waitpid() here. */ + waitpid(nagbar_pid, NULL, 0); +} + +static int json_boolean(void *ctx, int boolval) { + DLOG("Got bool: %d, success_key %d, nesting_level %d\n", boolval, success_key, current_nesting_level); + + if (success_key && current_nesting_level == 1 && !boolval) + command_failed = true; + + return 1; +} + +#if YAJL_MAJOR >= 2 +static int json_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) { +#else +static int json_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) { +#endif + success_key = (stringlen >= strlen("success") && + strncmp((const char*)stringval, "success", strlen("success")) == 0); + return 1; +} + +static int json_start_map(void *ctx) { + current_nesting_level++; + return 1; +} + +static int json_end_map(void *ctx) { + current_nesting_level--; + return 1; +} + +static yajl_callbacks command_error_callbacks = { + NULL, + &json_boolean, + NULL, + NULL, + NULL, + NULL, + &json_start_map, + &json_map_key, + &json_end_map, + NULL, + NULL +}; + +/* + * There was a key press. We compare this key code with our bindings table and pass + * the bound action to parse_command(). + * + */ +void handle_key_press(xcb_key_press_event_t *event) { + + last_timestamp = event->time; + + DLOG("Keypress %d, state raw = %d\n", event->detail, event->state); + + /* Remove the numlock bit, all other bits are modifiers we can bind to */ + uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); + DLOG("(removed numlock, state = %d)\n", state_filtered); + /* Only use the lower 8 bits of the state (modifier masks) so that mouse + * button masks are filtered out */ + state_filtered &= 0xFF; + DLOG("(removed upper 8 bits, state = %d)\n", state_filtered); + + if (xkb_current_group == XkbGroup2Index) + state_filtered |= BIND_MODE_SWITCH; + + DLOG("(checked mode_switch, state %d)\n", state_filtered); + + /* Find the binding */ + Binding *bind = get_binding(state_filtered, event->detail); + + /* No match? Then the user has Mode_switch enabled but does not have a + * specific keybinding. Fall back to the default keybindings (without + * Mode_switch). Makes it much more convenient for users of a hybrid + * layout (like us, ru). */ + if (bind == NULL) { + state_filtered &= ~(BIND_MODE_SWITCH); + DLOG("no match, new state_filtered = %d\n", state_filtered); + if ((bind = get_binding(state_filtered, event->detail)) == NULL) { + ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n", + state_filtered, event->detail); + return; + } + } + + char *command_copy = sstrdup(bind->command); + struct CommandResult *command_output = parse_command(command_copy); + free(command_copy); + + if (command_output->needs_tree_render) + tree_render(); + + /* We parse the JSON reply to figure out whether there was an error + * ("success" being false in on of the returned dictionaries). */ + const unsigned char *reply; +#if YAJL_MAJOR >= 2 + size_t length; + yajl_handle handle = yajl_alloc(&command_error_callbacks, NULL, NULL); +#else + unsigned int length; + yajl_parser_config parse_conf = { 0, 0 }; + + yajl_handle handle = yajl_alloc(&command_error_callbacks, &parse_conf, NULL, NULL); +#endif + yajl_gen_get_buf(command_output->json_gen, &reply, &length); + + current_nesting_level = 0; + success_key = false; + command_failed = false; + yajl_status state = yajl_parse(handle, reply, length); + if (state != yajl_status_ok) { + ELOG("Could not parse my own reply. That's weird. reply is %.*s\n", length, reply); + } else { + if (command_failed) + start_commanderror_nagbar(); + } + + yajl_free(handle); + + yajl_gen_free(command_output->json_gen); +} diff --git a/src/util.c b/src/util.c index d337963e..76a74db1 100644 --- a/src/util.c +++ b/src/util.c @@ -303,6 +303,7 @@ void i3_restart(bool forget_layout) { char *restart_filename = forget_layout ? NULL : store_restart_layout(); kill_configerror_nagbar(true); + kill_commanderror_nagbar(true); restore_geometry(); diff --git a/testcases/t/187-commands-parser.t b/testcases/t/187-commands-parser.t index 8b57a0a1..335c775d 100644 --- a/testcases/t/187-commands-parser.t +++ b/testcases/t/187-commands-parser.t @@ -12,7 +12,7 @@ sub parser_calls { # TODO: use a timeout, so that we can error out if it doesn’t terminate # TODO: better way of passing arguments - my $stdout = qx(../test.commands_parser '$command' 2>&-); + my $stdout = qx(../test.commands_parser '$command' 2>&1 >&-); # Filter out all debugging output. my @lines = split("\n", $stdout); @@ -127,15 +127,15 @@ is(parser_calls("\nworkspace test"), ################################################################################ is(parser_calls('unknown_literal'), - "Expected one of these tokens: , '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" . - "Your command: unknown_literal\n" . - " ^^^^^^^^^^^^^^^", + "ERROR: Expected one of these tokens: , '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" . + "ERROR: Your command: unknown_literal\n" . + "ERROR: ^^^^^^^^^^^^^^^", 'error for unknown literal ok'); is(parser_calls('move something to somewhere'), - "Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" . - "Your command: move something to somewhere\n" . - " ^^^^^^^^^^^^^^^^^^^^^^", + "ERROR: Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" . + "ERROR: Your command: move something to somewhere\n" . + "ERROR: ^^^^^^^^^^^^^^^^^^^^^^", 'error for unknown literal ok'); ################################################################################ From dbd248fe1faabd799d00807f79caf673cb0b8e0e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 01:10:45 +0200 Subject: [PATCH 059/200] makefile: store $I3_VERSION in dist tarballs This is necessary because the autobuilder uses a dist tarball to build i3 from. If we store $VERSION, the autobuiluder binaries will not run in developer mode, thus defeating the purpose of developer mode. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 54d843f3..f1104ead 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ dist: distclean # Only copy source code from i3-input mkdir i3-${VERSION}/i3-input find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; - echo -n '${VERSION} ($(shell git log --pretty=format:%cd --date=short -n1))' > i3-${VERSION}/VERSION + echo -n ${I3_VERSION} > i3-${VERSION}/VERSION # Pre-generate a manpage to allow distributors to skip this step and save some dependencies $(MAKE) mans cp man/*.1 i3-${VERSION}/man/ From 1b3435807a97df8329ab394189d2f8690bed1b00 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 3 Aug 2012 23:58:02 +0200 Subject: [PATCH 060/200] add missing \n in debug message --- src/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index 3ed392ac..bbcad7a4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -296,7 +296,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool } } else { - DLOG("not focusing because we're not killing anybody"); + DLOG("not focusing because we're not killing anybody\n"); } } else { DLOG("not focusing, was not mapped\n"); From 077e021e26e695288ad153fea16ba0e2528cef5c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 00:51:41 +0200 Subject: [PATCH 061/200] tests: implement --xtrace in complete-run.pl --- testcases/complete-run.pl | 7 +++++++ testcases/lib/SocketActivation.pm | 8 ++++++++ testcases/lib/TestWorker.pm | 3 ++- testcases/lib/i3test.pm | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 020e2f90..f5520603 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -46,6 +46,7 @@ my @displays = (); my %options = ( valgrind => 0, strace => 0, + xtrace => 0, coverage => 0, restart => 0, ); @@ -54,6 +55,7 @@ my $result = GetOptions( "coverage-testing" => \$options{coverage}, "valgrind" => \$options{valgrind}, "strace" => \$options{strace}, + "xtrace" => \$options{xtrace}, "display=s" => \@displays, "parallel=i" => \$parallel, "help|?" => \$help, @@ -342,6 +344,11 @@ C. Runs i3 under strace to trace system calls. The output will be available in C. +=item B<--xtrace> + +Runs i3 under xtrace to trace X11 requests/replies. The output will be +available in C. + =item B<--coverage-testing> Exits i3 cleanly (instead of kill -9) to make coverage testing work properly. diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm index 8f52bddc..0a062be4 100644 --- a/testcases/lib/SocketActivation.pm +++ b/testcases/lib/SocketActivation.pm @@ -124,6 +124,14 @@ sub activate_i3 { 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"'; } + if ($args{xtrace}) { + my $out = "$outdir/xtrace-for-$test.log"; + + # See comment in $args{strace} branch. + $cmd = qq|xtrace -n -o "$out" -- | . + 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"'; + } + # We need to use the shell due to using output redirections. exec '/bin/sh', '-c', $cmd; diff --git a/testcases/lib/TestWorker.pm b/testcases/lib/TestWorker.pm index 66f22bc0..a224b718 100644 --- a/testcases/lib/TestWorker.pm +++ b/testcases/lib/TestWorker.pm @@ -105,12 +105,13 @@ sub worker_wait { $test->failure_output(\*STDERR); $test->todo_output(\*STDOUT); - @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE COVERAGE RESTART)} + @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)} = ($self->{display}, basename($file), $outdir, $options->{valgrind}, $options->{strace}, + $options->{xtrace}, $options->{coverage}, $options->{restart}); diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index 32a17934..aff23009 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -547,6 +547,7 @@ sub launch_with_config { testname => $ENV{TESTNAME}, valgrind => $ENV{VALGRIND}, strace => $ENV{STRACE}, + xtrace => $ENV{XTRACE}, restart => $ENV{RESTART}, cv => $cv, dont_create_temp_dir => $args{dont_create_temp_dir}, From de94f6da1a9f7d2b9701035b844154658d9d308f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 03:04:00 +0200 Subject: [PATCH 062/200] Introduce splith/splitv layouts, remove orientation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this commit, the "default" layout is replaced by the splith and splitv layouts. splith is equivalent to default with orientation horizontal and splitv is equivalent to default with orientation vertical. The "split h" and "split v" commands continue to work as before, they split the current container and you will end up in a split container with layout splith (after "split h") or splitv (after "split v"). To change a splith container into a splitv container, use either "layout splitv" or "layout toggle split". The latter command is used in the default config as mod+l (previously "layout default"). In case you have "layout default" in your config file, it is recommended to just replace it by "layout toggle split", which will work as "layout default" did before when pressing it once, but toggle between horizontal/vertical when pressing it repeatedly. The rationale behind this commit is that it’s cleaner to have all parameters that influence how windows are rendered in the layout itself rather than having a special parameter in combination with only one layout. This enables us to change existing split containers in all cases without breaking existing features (see ticket #464). Also, users should feel more confident about whether they are actually splitting or just changing an existing split container now. As a nice side-effect, this commit brings back the "layout toggle" feature we once had in i3 version 3 (see the userguide). AFAIK, it is safe to use in-place restart to upgrade into versions after this commit (switching to an older version will break your layout, though). Fixes #464 --- docs/ipc | 7 +- docs/userguide | 66 +++++++++++------- i3.config | 4 +- i3.config.keycodes | 4 +- include/commands.h | 8 ++- include/con.h | 9 +++ include/data.h | 13 +++- parser-specs/commands.spec | 14 +++- src/click.c | 6 +- src/commands.c | 56 +++++++++++++--- src/con.c | 125 +++++++++++++++++++++++++++-------- src/floating.c | 7 +- src/ipc.c | 37 ++++++++--- src/load_layout.c | 45 ++++++++++--- src/randr.c | 11 ++- src/render.c | 12 ++-- src/tree.c | 28 +++++--- src/workspace.c | 62 ++++++++--------- testcases/t/116-nestedcons.t | 4 +- testcases/t/122-split.t | 10 +-- testcases/t/145-flattening.t | 2 +- testcases/t/192-layout.t | 84 +++++++++++++++++++++++ 22 files changed, 458 insertions(+), 156 deletions(-) create mode 100644 testcases/t/192-layout.t diff --git a/docs/ipc b/docs/ipc index 90718405..525e9968 100644 --- a/docs/ipc +++ b/docs/ipc @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -July 2012 +August 2012 This document describes how to interface with i3 from a separate process. This is useful for example to remote-control i3 (to write test cases for example) or @@ -270,12 +270,15 @@ border (string):: Can be either "normal", "none" or "1pixel", dependending on the container’s border style. layout (string):: - Can be either "default", "stacked", "tabbed", "dockarea" or "output". + Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or + "output". Other values might be possible in the future, should we add new layouts. orientation (string):: Can be either "none" (for non-split containers), "horizontal" or "vertical". + THIS FIELD IS OBSOLETE. It is still present, but your code should not + use it. Instead, rely on the layout field. percent (float):: The percentage which this container takes in its parent. A value of +null+ means that the percent property does not make sense for this diff --git a/docs/userguide b/docs/userguide index 1545ac08..64063831 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1,7 +1,7 @@ i3 User’s Guide =============== -Michael Stapelberg -April 2012 +Michael Stapelberg +August 2012 This document contains all the information you need to configure and use the i3 window manager. If it does not, please contact us on IRC (preferred) or post your @@ -68,9 +68,11 @@ To split a window vertically, press +mod+v+. To split it horizontally, press A split container can have one of the following layouts: -default:: +splith/splitv:: Windows are sized so that every window gets an equal amount of space in the -container. +container. splith distributes the windows horizontally (windows are right next +to each other), splitv distributes them vertically (windows are on top of each +other). stacking:: Only the focused window in the container is displayed. You get a list of windows at the top of the container. @@ -78,8 +80,8 @@ tabbed:: The same principle as +stacking+, but the list of windows at the top is only a single line which is vertically split. -To switch modes, press +mod+e+ for default, +mod+s+ for stacking and -+mod+w+ for tabbed. +To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for +stacking and +mod+w+ for tabbed. image:modes.png[Container modes] @@ -196,20 +198,21 @@ image::tree-shot4.png["shot4",title="Two terminals on standard workspace"] It is only natural to use so-called +Split Containers+ in order to build a layout when using a tree as data structure. In i3, every +Container+ has an -orientation (horizontal, vertical or unspecified). So, in our example with the -workspace, the default orientation of the workspace +Container+ is horizontal -(most monitors are widescreen nowadays). If you change the orientation to -vertical (+mod+v+ in the default config) and *then* open two terminals, i3 will -configure your windows like this: +orientation (horizontal, vertical or unspecified) and the orientation depends +on the layout the container is in (vertical for splitv and stacking, horizontal +for splith and tabbed). So, in our example with the workspace, the default +layout of the workspace +Container+ is splith (most monitors are widescreen +nowadays). If you change the layout to splitv (+mod+l+ in the default config) +and *then* open two terminals, i3 will configure your windows like this: image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"] -An interesting new feature of the tree branch is the ability to split anything: -Let’s assume you have two terminals on a workspace (with horizontal -orientation), focus is on the right terminal. Now you want to open another -terminal window below the current one. If you would just open a new terminal -window, it would show up to the right due to the horizontal workspace -orientation. Instead, press +mod+v+ to create a +Vertical Split Container+ (to +An interesting new feature of i3 since version 4 is the ability to split anything: +Let’s assume you have two terminals on a workspace (with splith layout, that is +horizontal orientation), focus is on the right terminal. Now you want to open +another terminal window below the current one. If you would just open a new +terminal window, it would show up to the right due to the splith layout. +Instead, press +mod+v+ to split the container with the splitv layout (to open a +Horizontal Split Container+, use +mod+h+). Now you can open a new terminal and it will open below the current one: @@ -1190,13 +1193,15 @@ cursor for 60 seconds. === Splitting containers The split command makes the current window a split container. Split containers -can contain multiple windows. Every split container has an orientation, it is -either split horizontally (a new window gets placed to the right of the current -one) or vertically (a new window gets placed below the current one). +can contain multiple windows. Depending on the layout of the split container, +new windows get placed to the right of the current one (splith) or new windows +get placed below the current one (splitv). If you apply this command to a split container with the same orientation, nothing will happen. If you use a different orientation, the split container’s -orientation will be changed (if it does not have more than one window). +orientation will be changed (if it does not have more than one window). Use ++layout toggle split+ to change the layout of any split container from splitv +to splith or vice-versa. *Syntax*: --------------------------- @@ -1211,19 +1216,32 @@ bindsym mod+h split horizontal === Manipulating layout -Use +layout default+, +layout stacking+ or +layout tabbed+ to change the -current container layout to default, stacking or tabbed layout, respectively. +Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the +current container layout to splith/splitv, stacking or tabbed layout, +respectively. To make the current window (!) fullscreen, use +fullscreen+, to make it floating (or tiling again) use +floating enable+ respectively +floating disable+ (or +floating toggle+): +*Syntax*: +-------------- +layout +layout toggle [split|all] +-------------- + *Examples*: -------------- bindsym mod+s layout stacking -bindsym mod+l layout default +bindsym mod+l layout toggle split bindsym mod+w layout tabbed +# Toggle between stacking/tabbed/split: +bindsym mod+x layout toggle + +# Toggle between stacking/tabbed/splith/splitv: +bindsym mod+x layout toggle all + # Toggle fullscreen bindsym mod+f fullscreen diff --git a/i3.config b/i3.config index 3225b973..d29155ed 100644 --- a/i3.config +++ b/i3.config @@ -57,10 +57,10 @@ bindsym Mod1+v split v # enter fullscreen mode for the focused container bindsym Mod1+f fullscreen -# change container layout (stacked, tabbed, default) +# change container layout (stacked, tabbed, toggle split) bindsym Mod1+s layout stacking bindsym Mod1+w layout tabbed -bindsym Mod1+e layout default +bindsym Mod1+e layout toggle split # toggle tiling / floating bindsym Mod1+Shift+space floating toggle diff --git a/i3.config.keycodes b/i3.config.keycodes index 2f014183..1bff8890 100644 --- a/i3.config.keycodes +++ b/i3.config.keycodes @@ -58,10 +58,10 @@ bindcode $mod+55 split v # enter fullscreen mode for the focused container bindcode $mod+41 fullscreen -# change container layout (stacked, tabbed, default) +# change container layout (stacked, tabbed, toggle split) bindcode $mod+39 layout stacking bindcode $mod+25 layout tabbed -bindcode $mod+26 layout default +bindcode $mod+26 layout toggle split # toggle tiling / floating bindcode $mod+Shift+65 floating toggle diff --git a/include/commands.h b/include/commands.h index 85057d19..37ee98d9 100644 --- a/include/commands.h +++ b/include/commands.h @@ -200,11 +200,17 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode); void cmd_move_direction(I3_CMD, char *direction, char *move_px); /** - * Implementation of 'layout default|stacked|stacking|tabbed'. + * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'. * */ void cmd_layout(I3_CMD, char *layout_str); +/** + * Implementation of 'layout toggle [all|split]'. + * + */ +void cmd_layout_toggle(I3_CMD, char *toggle_mode); + /** * Implementaiton of 'exit'. * diff --git a/include/con.h b/include/con.h index 95726147..1965da7c 100644 --- a/include/con.h +++ b/include/con.h @@ -248,6 +248,15 @@ void con_set_border_style(Con *con, int border_style); */ void con_set_layout(Con *con, int layout); +/** + * This function toggles the layout of a given container. toggle_mode can be + * either 'default' (toggle only between stacked/tabbed/last_split_layout), + * 'split' (toggle only between splitv/splith) or 'all' (toggle between all + * layouts). + * + */ +void con_toggle_layout(Con *con, const char *toggle_mode); + /** * Determines the minimum size of the given con by looking at its children (for * split/stacked/tabbed cons). Will be called when resizing floating cons diff --git a/include/data.h b/include/data.h index f4ed9a3e..d28f1756 100644 --- a/include/data.h +++ b/include/data.h @@ -423,6 +423,8 @@ struct Assignment { */ struct Con { bool mapped; + /** whether this is a split container or not */ + bool split; enum { CT_ROOT = 0, CT_OUTPUT = 1, @@ -431,7 +433,6 @@ struct Con { CT_WORKSPACE = 4, CT_DOCKAREA = 5 } type; - orientation_t orientation; struct Con *parent; struct Rect rect; @@ -496,7 +497,15 @@ struct Con { TAILQ_HEAD(swallow_head, Match) swallow_head; enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode; - enum { L_DEFAULT = 0, L_STACKED = 1, L_TABBED = 2, L_DOCKAREA = 3, L_OUTPUT = 4 } layout; + enum { + L_DEFAULT = 0, + L_STACKED = 1, + L_TABBED = 2, + L_DOCKAREA = 3, + L_OUTPUT = 4, + L_SPLITV = 5, + L_SPLITH = 6 + } layout, last_split_layout; border_style_t border_style; /** floating? (= not in tiling layout) This cannot be simply a bool * because we want to keep track of whether the status was set by the diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index b416d968..b4c9e005 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -66,10 +66,20 @@ state BORDER: border_style = 'normal', 'none', '1pixel', 'toggle' -> call cmd_border($border_style) -# layout default|stacked|stacking|tabbed +# layout default|stacked|stacking|tabbed|splitv|splith +# layout toggle [split|all] state LAYOUT: - layout_mode = 'default', 'stacked', 'stacking', 'tabbed' + layout_mode = 'default', 'stacked', 'stacking', 'tabbed', 'splitv', 'splith' -> call cmd_layout($layout_mode) + 'toggle' + -> LAYOUT_TOGGLE + +# layout toggle [split|all] +state LAYOUT_TOGGLE: + end + -> call cmd_layout_toggle($toggle_mode) + toggle_mode = 'split', 'all' + -> call cmd_layout_toggle($toggle_mode) # append_layout state APPEND_LAYOUT: diff --git a/src/click.c b/src/click.c index ca2a1037..9f0549fa 100644 --- a/src/click.c +++ b/src/click.c @@ -35,13 +35,13 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press Con *resize_con = con; while (resize_con->type != CT_WORKSPACE && resize_con->type != CT_FLOATING_CON && - resize_con->parent->orientation != orientation) + con_orientation(resize_con->parent) != orientation) resize_con = resize_con->parent; DLOG("resize_con = %p\n", resize_con); if (resize_con->type != CT_WORKSPACE && resize_con->type != CT_FLOATING_CON && - resize_con->parent->orientation == orientation) { + con_orientation(resize_con->parent) == orientation) { first = resize_con; second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes); if (second == TAILQ_END(&(first->nodes_head))) { @@ -145,7 +145,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click if ((check_con->layout == L_STACKED || check_con->layout == L_TABBED || - check_con->orientation == HORIZ) && + con_orientation(check_con) == HORIZ) && con_num_children(check_con) > 1) { DLOG("Not handling this resize, this container has > 1 child.\n"); return false; diff --git a/src/commands.c b/src/commands.c index c0681d26..a1ef3bbf 100644 --- a/src/commands.c +++ b/src/commands.c @@ -530,7 +530,7 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); do { - if (current->parent->orientation != search_orientation) { + if (con_orientation(current->parent) != search_orientation) { current = current->parent; continue; } @@ -541,7 +541,7 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int percentage = 1.0 / children; LOG("default percentage = %f\n", percentage); - orientation_t orientation = current->parent->orientation; + orientation_t orientation = con_orientation(current->parent); if ((orientation == HORIZ && (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || @@ -612,7 +612,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i while (current->type != CT_WORKSPACE && current->type != CT_FLOATING_CON && - current->parent->orientation != search_orientation) + con_orientation(current->parent) != search_orientation) current = current->parent; /* get the default percentage */ @@ -621,7 +621,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i double percentage = 1.0 / children; LOG("default percentage = %f\n", percentage); - orientation_t orientation = current->parent->orientation; + orientation_t orientation = con_orientation(current->parent); if ((orientation == HORIZ && strcmp(direction, "height") == 0) || @@ -1397,17 +1397,27 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) { } /* - * Implementation of 'layout default|stacked|stacking|tabbed'. + * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'. * */ void cmd_layout(I3_CMD, char *layout_str) { if (strcmp(layout_str, "stacking") == 0) layout_str = "stacked"; - DLOG("changing layout to %s\n", layout_str); owindow *current; - int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT : - (strcmp(layout_str, "stacked") == 0 ? L_STACKED : - L_TABBED)); + int layout; + /* default is a special case which will be handled in con_set_layout(). */ + if (strcmp(layout_str, "default") == 0) + layout = L_DEFAULT; + else if (strcmp(layout_str, "stacked") == 0) + layout = L_STACKED; + else if (strcmp(layout_str, "tabbed") == 0) + layout = L_TABBED; + else if (strcmp(layout_str, "splitv") == 0) + layout = L_SPLITV; + else if (strcmp(layout_str, "splith") == 0) + layout = L_SPLITH; + + DLOG("changing layout to %s (%d)\n", layout_str, layout); /* check if the match is empty, not if the result is empty */ if (match_is_empty(current_match)) @@ -1424,6 +1434,33 @@ void cmd_layout(I3_CMD, char *layout_str) { ysuccess(true); } +/* + * Implementation of 'layout toggle [all|split]'. + * + */ +void cmd_layout_toggle(I3_CMD, char *toggle_mode) { + owindow *current; + + if (toggle_mode == NULL) + toggle_mode = "default"; + + DLOG("toggling layout (mode = %s)\n", toggle_mode); + + /* check if the match is empty, not if the result is empty */ + if (match_is_empty(current_match)) + con_toggle_layout(focused->parent, toggle_mode); + else { + TAILQ_FOREACH(current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); + con_toggle_layout(current->con, toggle_mode); + } + } + + cmd_output->needs_tree_render = true; + // XXX: default reply for now, make this a better reply + ysuccess(true); +} + /* * Implementaiton of 'exit'. * @@ -1472,6 +1509,7 @@ void cmd_restart(I3_CMD) { void cmd_open(I3_CMD) { LOG("opening new container\n"); Con *con = tree_open_con(NULL, NULL); + con->layout = L_SPLITH; con_focus(con); y(map_open); diff --git a/src/con.c b/src/con.c index f804a204..ccdd5682 100644 --- a/src/con.c +++ b/src/con.c @@ -217,8 +217,8 @@ bool con_accepts_window(Con *con) { if (con->type == CT_WORKSPACE) return false; - if (con->orientation != NO_ORIENTATION) { - DLOG("container %p does not accepts windows, orientation != NO_ORIENTATION\n", con); + if (con->split) { + DLOG("container %p does not accept windows, it is a split container.\n", con); return false; } @@ -265,8 +265,11 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) { while (con_orientation(parent) != orientation) { DLOG("Need to go one level further up\n"); parent = parent->parent; - /* Abort when we reach a floating con */ - if (parent && parent->type == CT_FLOATING_CON) + /* Abort when we reach a floating con, or an output con */ + if (parent && + (parent->type == CT_FLOATING_CON || + parent->type == CT_OUTPUT || + (parent->parent && parent->parent->type == CT_OUTPUT))) parent = NULL; if (parent == NULL) break; @@ -697,14 +700,32 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool * */ int con_orientation(Con *con) { - /* stacking containers behave like they are in vertical orientation */ - if (con->layout == L_STACKED) - return VERT; + switch (con->layout) { + case L_SPLITV: + /* stacking containers behave like they are in vertical orientation */ + case L_STACKED: + return VERT; - if (con->layout == L_TABBED) - return HORIZ; + case L_SPLITH: + /* tabbed containers behave like they are in vertical orientation */ + case L_TABBED: + return HORIZ; - return con->orientation; + case L_DEFAULT: + DLOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n"); + assert(false); + return HORIZ; + + case L_DOCKAREA: + case L_OUTPUT: + DLOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con); + assert(false); + return HORIZ; + + default: + DLOG("con_orientation() ran into default\n"); + assert(false); + } } /* @@ -1017,23 +1038,16 @@ void con_set_layout(Con *con, int layout) { Con *new = con_new(NULL, NULL); new->parent = con; - /* 2: set the requested layout on the split con */ + /* 2: Set the requested layout on the split container and mark it as + * split. */ new->layout = layout; - - /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs - * to be set. Otherwise, this con will not be interpreted as a split - * container. */ - if (config.default_orientation == NO_ORIENTATION) { - new->orientation = (con->rect.height > con->rect.width) ? VERT : HORIZ; - } else { - new->orientation = config.default_orientation; - } + new->split = true; Con *old_focused = TAILQ_FIRST(&(con->focus_head)); if (old_focused == TAILQ_END(&(con->focus_head))) old_focused = NULL; - /* 4: move the existing cons of this workspace below the new con */ + /* 3: move the existing cons of this workspace below the new con */ DLOG("Moving cons\n"); Con *child; while (!TAILQ_EMPTY(&(con->nodes_head))) { @@ -1054,7 +1068,66 @@ void con_set_layout(Con *con, int layout) { return; } - con->layout = layout; + if (layout == L_DEFAULT) { + /* Special case: the layout formerly known as "default" (in combination + * with an orientation). Since we switched to splith/splitv layouts, + * using the "default" layout (which "only" should happen when using + * legacy configs) is using the last split layout (either splith or + * splitv) in order to still do the same thing. + * + * Starting from v4.6 though, we will nag users about using "layout + * default", and in v4.9 we will remove it entirely (with an + * appropriate i3-migrate-config mechanism). */ + con->layout = con->last_split_layout; + } else { + /* We fill in last_split_layout when switching to a different layout + * since there are many places in the code that don’t use + * con_set_layout(). */ + if (con->layout == L_SPLITH || con->layout == L_SPLITV) + con->last_split_layout = con->layout; + con->layout = layout; + } +} + +/* + * This function toggles the layout of a given container. toggle_mode can be + * either 'default' (toggle only between stacked/tabbed/last_split_layout), + * 'split' (toggle only between splitv/splith) or 'all' (toggle between all + * layouts). + * + */ +void con_toggle_layout(Con *con, const char *toggle_mode) { + if (strcmp(toggle_mode, "split") == 0) { + /* Toggle between splits. When the current layout is not a split + * layout, we just switch back to last_split_layout. Otherwise, we + * change to the opposite split layout. */ + if (con->layout != L_SPLITH && con->layout != L_SPLITV) + con_set_layout(con, con->last_split_layout); + else { + if (con->layout == L_SPLITH) + con_set_layout(con, L_SPLITV); + else con_set_layout(con, L_SPLITH); + } + } else { + if (con->layout == L_STACKED) + con_set_layout(con, L_TABBED); + else if (con->layout == L_TABBED) { + if (strcmp(toggle_mode, "all") == 0) + con_set_layout(con, L_SPLITH); + else con_set_layout(con, con->last_split_layout); + } else if (con->layout == L_SPLITH || con->layout == L_SPLITV) { + if (strcmp(toggle_mode, "all") == 0) { + /* When toggling through all modes, we toggle between + * splith/splitv, whereas normally we just directly jump to + * stacked. */ + if (con->layout == L_SPLITH) + con_set_layout(con, L_SPLITV); + else con_set_layout(con, L_STACKED); + } else { + con_set_layout(con, L_STACKED); + } + } + } } /* @@ -1131,12 +1204,12 @@ Rect con_minimum_size(Con *con) { /* For horizontal/vertical split containers we sum up the width (h-split) * or height (v-split) and use the maximum of the height (h-split) or width * (v-split) as minimum size. */ - if (con->orientation == HORIZ || con->orientation == VERT) { + if (con->split) { uint32_t width = 0, height = 0; Con *child; TAILQ_FOREACH(child, &(con->nodes_head), nodes) { Rect min = con_minimum_size(child); - if (con->orientation == HORIZ) { + if (con->layout == L_SPLITH) { width += min.width; height = max(height, min.height); } else { @@ -1148,8 +1221,8 @@ Rect con_minimum_size(Con *con) { return (Rect){ 0, 0, width, height }; } - ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n", - con->type, con->layout, con->orientation); + ELOG("Unhandled case, type = %d, layout = %d, split = %d\n", + con->type, con->layout, con->split); assert(false); } diff --git a/src/floating.c b/src/floating.c index c0154aca..90c4d560 100644 --- a/src/floating.c +++ b/src/floating.c @@ -40,7 +40,7 @@ void floating_enable(Con *con, bool automatic) { } /* 1: If the container is a workspace container, we need to create a new - * split-container with the same orientation and make that one floating. We + * split-container with the same layout and make that one floating. We * cannot touch the workspace container itself because floating containers * are children of the workspace. */ if (con->type == CT_WORKSPACE) { @@ -52,7 +52,7 @@ void floating_enable(Con *con, bool automatic) { /* TODO: refactor this with src/con.c:con_set_layout */ Con *new = con_new(NULL, NULL); new->parent = con; - new->orientation = con->orientation; + new->layout = con->layout; /* since the new container will be set into floating mode directly * afterwards, we need to copy the workspace rect. */ @@ -97,8 +97,9 @@ void floating_enable(Con *con, bool automatic) { * otherwise. */ Con *ws = con_get_workspace(con); nc->parent = ws; - nc->orientation = NO_ORIENTATION; + nc->split = true; nc->type = CT_FLOATING_CON; + nc->layout = L_SPLITH; /* 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()) even though it’s not. */ diff --git a/src/ipc.c b/src/ipc.c index 60ce8145..1d19fc6c 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -161,17 +161,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("type"); y(integer, con->type); + /* provided for backwards compatibility only. */ ystr("orientation"); - switch (con->orientation) { - case NO_ORIENTATION: - ystr("none"); - break; - case HORIZ: + if (!con->split) + ystr("none"); + else { + if (con_orientation(con) == HORIZ) ystr("horizontal"); - break; - case VERT: - ystr("vertical"); - break; + else ystr("vertical"); } ystr("scratchpad_state"); @@ -203,10 +200,20 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focused"); y(bool, (con == focused)); + ystr("split"); + y(bool, con->split); + ystr("layout"); switch (con->layout) { case L_DEFAULT: - ystr("default"); + DLOG("About to dump layout=default, this is a bug in the code.\n"); + assert(false); + break; + case L_SPLITV: + ystr("splitv"); + break; + case L_SPLITH: + ystr("splith"); break; case L_STACKED: ystr("stacked"); @@ -222,6 +229,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { break; } + ystr("last_split_layout"); + switch (con->layout) { + case L_SPLITV: + ystr("splitv"); + break; + default: + ystr("splith"); + break; + } + ystr("border"); switch (con->border_style) { case BS_NORMAL: diff --git a/src/load_layout.c b/src/load_layout.c index a8063dca..54735d91 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -156,15 +156,25 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { memcpy(json_node->sticky_group, val, len); LOG("sticky_group of this container is %s\n", json_node->sticky_group); } else if (strcasecmp(last_key, "orientation") == 0) { + /* Upgrade path from older versions of i3 (doing an inplace restart + * to a newer version): + * "orientation" is dumped before "layout". Therefore, we store + * whether the orientation was horizontal or vertical in the + * last_split_layout. When we then encounter layout == "default", + * we will use the last_split_layout as layout instead. */ char *buf = NULL; sasprintf(&buf, "%.*s", (int)len, val); - if (strcasecmp(buf, "none") == 0) - json_node->orientation = NO_ORIENTATION; - else if (strcasecmp(buf, "horizontal") == 0) - json_node->orientation = HORIZ; + if (strcasecmp(buf, "none") == 0 || + strcasecmp(buf, "horizontal") == 0) + json_node->last_split_layout = L_SPLITH; else if (strcasecmp(buf, "vertical") == 0) - json_node->orientation = VERT; + json_node->last_split_layout = L_SPLITV; else LOG("Unhandled orientation: %s\n", buf); + + /* What used to be an implicit check whether orientation != + * NO_ORIENTATION is now a proper separate flag. */ + if (strcasecmp(buf, "none") != 0) + json_node->split = true; free(buf); } else if (strcasecmp(last_key, "border") == 0) { char *buf = NULL; @@ -181,17 +191,33 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { char *buf = NULL; sasprintf(&buf, "%.*s", (int)len, val); if (strcasecmp(buf, "default") == 0) - json_node->layout = L_DEFAULT; + /* This set above when we read "orientation". */ + json_node->layout = json_node->last_split_layout; else if (strcasecmp(buf, "stacked") == 0) json_node->layout = L_STACKED; else if (strcasecmp(buf, "tabbed") == 0) json_node->layout = L_TABBED; - else if (strcasecmp(buf, "dockarea") == 0) + else if (strcasecmp(buf, "dockarea") == 0) { json_node->layout = L_DOCKAREA; - else if (strcasecmp(buf, "output") == 0) + /* Necessary for migrating from older versions of i3. */ + json_node->split = false; + } else if (strcasecmp(buf, "output") == 0) json_node->layout = L_OUTPUT; + else if (strcasecmp(buf, "splith") == 0) + json_node->layout = L_SPLITH; + else if (strcasecmp(buf, "splitv") == 0) + json_node->layout = L_SPLITV; else LOG("Unhandled \"layout\": %s\n", buf); free(buf); + } else if (strcasecmp(last_key, "last_split_layout") == 0) { + char *buf = NULL; + sasprintf(&buf, "%.*s", (int)len, val); + if (strcasecmp(buf, "splith") == 0) + json_node->last_split_layout = L_SPLITH; + else if (strcasecmp(buf, "splitv") == 0) + json_node->last_split_layout = L_SPLITV; + else LOG("Unhandled \"last_splitlayout\": %s\n", buf); + free(buf); } else if (strcasecmp(last_key, "mark") == 0) { char *buf = NULL; sasprintf(&buf, "%.*s", (int)len, val); @@ -288,6 +314,9 @@ static int json_bool(void *ctx, int val) { to_focus = json_node; } + if (strcasecmp(last_key, "split") == 0) + json_node->split = val; + if (parsing_swallows) { if (strcasecmp(last_key, "restart_mode") == 0) current_swallow->restart_mode = val; diff --git a/src/randr.c b/src/randr.c index 73adbf0e..f69d15a8 100644 --- a/src/randr.c +++ b/src/randr.c @@ -256,7 +256,6 @@ void output_init_con(Output *output) { Con *topdock = con_new(NULL, NULL); topdock->type = CT_DOCKAREA; topdock->layout = L_DOCKAREA; - topdock->orientation = VERT; /* this container swallows dock clients */ Match *match = scalloc(sizeof(Match)); match_init(match); @@ -278,6 +277,7 @@ void output_init_con(Output *output) { DLOG("adding main content container\n"); Con *content = con_new(NULL, NULL); content->type = CT_CON; + content->layout = L_SPLITH; FREE(content->name); content->name = sstrdup("content"); @@ -290,7 +290,6 @@ void output_init_con(Output *output) { Con *bottomdock = con_new(NULL, NULL); bottomdock->type = CT_DOCKAREA; bottomdock->layout = L_DOCKAREA; - bottomdock->orientation = VERT; /* this container swallows dock clients */ match = scalloc(sizeof(Match)); match_init(match); @@ -447,11 +446,11 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) { if (con_num_children(workspace) > 1) continue; - workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ; - DLOG("Setting workspace [%d,%s]'s orientation to %d.\n", workspace->num, workspace->name, workspace->orientation); + workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH; + DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout); if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) { - child->orientation = workspace->orientation; - DLOG("Setting child [%d,%s]'s orientation to %d.\n", child->num, child->name, child->orientation); + child->layout = workspace->layout; + DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout); } } } diff --git a/src/render.c b/src/render.c index 860219df..bad67f04 100644 --- a/src/render.c +++ b/src/render.c @@ -106,9 +106,9 @@ static void render_l_output(Con *con) { */ void render_con(Con *con, bool render_fullscreen) { int children = con_num_children(con); - DLOG("Rendering %snode %p / %s / layout %d / children %d / orient %d\n", + DLOG("Rendering %snode %p / %s / layout %d / children %d\n", (render_fullscreen ? "fullscreen " : ""), con, con->name, con->layout, - children, con->orientation); + children); /* Copy container rect, subtract container border */ /* This is the actually usable space inside this container for clients */ @@ -208,11 +208,11 @@ void render_con(Con *con, bool render_fullscreen) { /* precalculate the sizes to be able to correct rounding errors */ int sizes[children]; - if (con->layout == L_DEFAULT && children > 0) { + if ((con->layout == L_SPLITH || con->layout == L_SPLITV) && children > 0) { assert(!TAILQ_EMPTY(&con->nodes_head)); Con *child; int i = 0, assigned = 0; - int total = con->orientation == HORIZ ? rect.width : rect.height; + int total = con_orientation(con) == HORIZ ? rect.width : rect.height; TAILQ_FOREACH(child, &(con->nodes_head), nodes) { double percentage = child->percent > 0.0 ? child->percent : 1.0 / children; assigned += sizes[i++] = percentage * total; @@ -289,8 +289,8 @@ void render_con(Con *con, bool render_fullscreen) { assert(children > 0); /* default layout */ - if (con->layout == L_DEFAULT) { - if (con->orientation == HORIZ) { + if (con->layout == L_SPLITH || con->layout == L_SPLITV) { + if (con->layout == L_SPLITH) { child->rect.x = x; child->rect.y = y; child->rect.width = sizes[i]; diff --git a/src/tree.c b/src/tree.c index bbcad7a4..068b0570 100644 --- a/src/tree.c +++ b/src/tree.c @@ -39,6 +39,7 @@ static Con *_create___i3(void) { content->type = CT_CON; FREE(content->name); content->name = sstrdup("content"); + content->layout = L_SPLITH; x_set_name(content, "[i3 con] content __i3"); con_attach(content, __i3, false); @@ -48,6 +49,7 @@ static Con *_create___i3(void) { ws->type = CT_WORKSPACE; ws->num = -1; ws->name = sstrdup("__i3_scratch"); + ws->layout = L_SPLITH; con_attach(ws, content, false); x_set_name(ws, "[i3 con] workspace __i3_scratch"); ws->fullscreen_mode = CF_OUTPUT; @@ -112,6 +114,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) { FREE(croot->name); croot->name = "root"; croot->type = CT_ROOT; + croot->layout = L_SPLITH; croot->rect = (Rect){ geometry->x, geometry->y, @@ -151,6 +154,7 @@ Con *tree_open_con(Con *con, i3Window *window) { /* 3. create the container and attach it to its parent */ Con *new = con_new(con, window); + new->layout = L_SPLITH; /* 4: re-calculate child->percent for each child */ con_fix_percent(con); @@ -337,7 +341,7 @@ void tree_split(Con *con, orientation_t orientation) { /* for a workspace, we just need to change orientation */ if (con->type == CT_WORKSPACE) { DLOG("Workspace, simply changing orientation to %d\n", orientation); - con->orientation = orientation; + con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; return; } @@ -351,8 +355,9 @@ void tree_split(Con *con, orientation_t orientation) { * child (its split functionality is unused so far), we just change the * orientation (more intuitive than splitting again) */ if (con_num_children(parent) == 1 && - parent->layout == L_DEFAULT) { - parent->orientation = orientation; + (parent->layout == L_SPLITH || + parent->layout == L_SPLITV)) { + parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; DLOG("Just changing orientation of existing container\n"); return; } @@ -364,7 +369,8 @@ void tree_split(Con *con, orientation_t orientation) { TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes); TAILQ_REPLACE(&(parent->focus_head), con, new, focused); new->parent = parent; - new->orientation = orientation; + new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; + new->split = true; /* 3: swap 'percent' (resize factor) */ new->percent = con->percent; @@ -594,7 +600,9 @@ void tree_flatten(Con *con) { DLOG("Checking if I can flatten con = %p / %s\n", con, con->name); /* We only consider normal containers without windows */ - if (con->type != CT_CON || con->window != NULL) + if (con->type != CT_CON || + parent->layout == L_OUTPUT || /* con == "content" */ + con->window != NULL) goto recurse; /* Ensure it got only one child */ @@ -602,12 +610,14 @@ void tree_flatten(Con *con) { if (child == NULL || TAILQ_NEXT(child, nodes) != NULL) goto recurse; + DLOG("child = %p, con = %p, parent = %p\n", child, con, parent); + /* The child must have a different orientation than the con but the same as * the con’s parent to be redundant */ - if (con->orientation == NO_ORIENTATION || - child->orientation == NO_ORIENTATION || - con->orientation == child->orientation || - child->orientation != parent->orientation) + if (con->split || + child->split || + con_orientation(con) == con_orientation(child) || + con_orientation(child) != con_orientation(parent)) goto recurse; DLOG("Alright, I have to flatten this situation now. Stay calm.\n"); diff --git a/src/workspace.c b/src/workspace.c index 3d08fa4c..6394084a 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -14,6 +14,25 @@ * back-and-forth switching. */ static char *previous_workspace_name = NULL; +/* + * Sets ws->layout to splith/splitv if default_orientation was specified in the + * configfile. Otherwise, it uses splith/splitv depending on whether the output + * is higher than wide. + * + */ +static void _workspace_apply_default_orientation(Con *ws) { + /* If default_orientation is set to NO_ORIENTATION we determine + * orientation depending on output resolution. */ + if (config.default_orientation == NO_ORIENTATION) { + Con *output = con_get_output(ws); + ws->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH; + DLOG("Auto orientation. Workspace size set to (%d,%d), setting layout to %d.\n", + output->rect.width, output->rect.height, ws->layout); + } else { + ws->layout = (config.default_orientation == HORIZ) ? L_SPLITH : L_SPLITV; + } +} + /* * Returns a pointer to the workspace with the given number (starting at 0), * creating the workspace if necessary (by allocating the necessary amount of @@ -64,16 +83,8 @@ Con *workspace_get(const char *num, bool *created) { else workspace->num = parsed_num; LOG("num = %d\n", workspace->num); - /* If default_orientation is set to NO_ORIENTATION we - * determine workspace orientation from workspace size. - * Otherwise we just set the orientation to default_orientation. */ - if (config.default_orientation == NO_ORIENTATION) { - workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ; - DLOG("Auto orientation. Output resolution set to (%d,%d), setting orientation to %d.\n", - workspace->rect.width, workspace->rect.height, workspace->orientation); - } else { - workspace->orientation = config.default_orientation; - } + workspace->parent = content; + _workspace_apply_default_orientation(workspace); con_attach(workspace, content, false); @@ -198,19 +209,12 @@ Con *create_workspace_on_output(Output *output, Con *content) { ws->fullscreen_mode = CF_OUTPUT; - /* If default_orientation is set to NO_ORIENTATION we determine - * orientation depending on output resolution. */ - if (config.default_orientation == NO_ORIENTATION) { - ws->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ; - DLOG("Auto orientation. Workspace size set to (%d,%d), setting orientation to %d.\n", - output->rect.width, output->rect.height, ws->orientation); - } else { - ws->orientation = config.default_orientation; - } + _workspace_apply_default_orientation(ws); return ws; } + /* * Returns true if the workspace is currently visible. Especially important for * multi-monitor environments, as they can have multiple currenlty active @@ -686,8 +690,7 @@ void workspace_update_urgent_flag(Con *ws) { /* * 'Forces' workspace orientation by moving all cons into a new split-con with - * the same orientation as the workspace and then changing the workspace - * orientation. + * the same layout as the workspace and then changing the workspace layout. * */ void ws_force_orientation(Con *ws, orientation_t orientation) { @@ -695,9 +698,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { Con *split = con_new(NULL, NULL); split->parent = ws; - /* 2: copy layout and orientation from workspace */ + /* 2: copy layout from workspace */ split->layout = ws->layout; - split->orientation = ws->orientation; Con *old_focused = TAILQ_FIRST(&(ws->focus_head)); @@ -709,8 +711,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { con_attach(child, split, true); } - /* 4: switch workspace orientation */ - ws->orientation = orientation; + /* 4: switch workspace layout */ + ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; /* 5: attach the new split container to the workspace */ DLOG("Attaching new split to ws\n"); @@ -745,19 +747,11 @@ Con *workspace_attach_to(Con *ws) { /* 1: create a new split container */ Con *new = con_new(NULL, NULL); new->parent = ws; + new->split = true; /* 2: set the requested layout on the split con */ new->layout = config.default_layout; - /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs - * to be set. Otherwise, this con will not be interpreted as a split - * container. */ - if (config.default_orientation == NO_ORIENTATION) { - new->orientation = (ws->rect.height > ws->rect.width) ? VERT : HORIZ; - } else { - new->orientation = config.default_orientation; - } - /* 4: attach the new split container to the workspace */ DLOG("Attaching new split %p to workspace %p\n", new, ws); con_attach(new, ws, false); diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 3a495e27..18e21019 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -39,14 +39,16 @@ my $expected = { name => 'root', orientation => $ignore, type => 0, + split => JSON::XS::false, id => $ignore, rect => $ignore, window_rect => $ignore, geometry => $ignore, swallows => $ignore, percent => undef, - layout => 'default', + layout => 'splith', floating => 'auto_off', + last_split_layout => 'splith', scratchpad_state => 'none', focus => $ignore, focused => JSON::XS::false, diff --git a/testcases/t/122-split.t b/testcases/t/122-split.t index f672e9d6..d491c37a 100644 --- a/testcases/t/122-split.t +++ b/testcases/t/122-split.t @@ -19,10 +19,10 @@ sub verify_split_layout { $tmp = fresh_workspace; $ws = get_ws($tmp); - is($ws->{orientation}, 'horizontal', 'orientation horizontal by default'); + is($ws->{layout}, 'splith', 'orientation horizontal by default'); cmd 'split v'; $ws = get_ws($tmp); - is($ws->{orientation}, 'vertical', 'split v changes workspace orientation'); + is($ws->{layout}, 'splitv', 'split v changes workspace orientation'); cmd 'open'; cmd 'open'; @@ -47,7 +47,7 @@ sub verify_split_layout { is(@{$first->{nodes}}, 0, 'first container has no children'); isnt($second->{name}, $old_name, 'second container was replaced'); - is($second->{orientation}, 'horizontal', 'orientation is horizontal'); + is($second->{layout}, 'splith', 'orientation is horizontal'); is(@{$second->{nodes}}, 2, 'second container has 2 children'); is($second->{nodes}->[0]->{name}, $old_name, 'found old second container'); } @@ -66,10 +66,10 @@ verify_split_layout(split_command => 'split horizontal'); $tmp = fresh_workspace; $ws = get_ws($tmp); -is($ws->{orientation}, 'horizontal', 'orientation horizontal by default'); +is($ws->{layout}, 'splith', 'orientation horizontal by default'); cmd 'split v'; $ws = get_ws($tmp); -is($ws->{orientation}, 'vertical', 'split v changes workspace orientation'); +is($ws->{layout}, 'splitv', 'split v changes workspace orientation'); cmd 'open'; my @content = @{get_ws_content($tmp)}; diff --git a/testcases/t/145-flattening.t b/testcases/t/145-flattening.t index 9d22afc3..dbd1f246 100644 --- a/testcases/t/145-flattening.t +++ b/testcases/t/145-flattening.t @@ -22,7 +22,7 @@ cmd 'move up'; cmd 'move right'; my $ws = get_ws($tmp); -is($ws->{orientation}, 'horizontal', 'workspace orientation is horizontal'); +is($ws->{layout}, 'splith', 'workspace layout is splith'); is(@{$ws->{nodes}}, 3, 'all three windows on workspace level'); done_testing; diff --git a/testcases/t/192-layout.t b/testcases/t/192-layout.t new file mode 100644 index 00000000..e410d513 --- /dev/null +++ b/testcases/t/192-layout.t @@ -0,0 +1,84 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verifies that switching between the different layouts works as expected. +use i3test; + +my $tmp = fresh_workspace; + +open_window; +open_window; +cmd 'split v'; +open_window; + +my ($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splitv', 'layout is splitv currently'); + +cmd 'layout stacked'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'stacked', 'layout now stacked'); + +cmd 'layout tabbed'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed'); + +cmd 'layout toggle split'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splitv', 'layout now splitv again'); + +cmd 'layout toggle split'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splith', 'layout now splith'); + +cmd 'layout toggle split'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splitv', 'layout now splitv'); + +cmd 'layout toggle split'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splith', 'layout now splith'); + +cmd 'layout toggle'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'stacked', 'layout now stacked'); + +cmd 'layout toggle'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed'); + +cmd 'layout toggle'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splith', 'layout now splith'); + +cmd 'layout toggle'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'stacked', 'layout now stacked'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splith', 'layout now splith'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splitv', 'layout now splitv'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'stacked', 'layout now stacked'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splith', 'layout now splith'); + +cmd 'layout toggle all'; +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[1]->{layout}, 'splitv', 'layout now splitv'); + +done_testing; From 79083f3b34d912830cd60b95cf8cbb1c9d95b91e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 03:43:12 +0200 Subject: [PATCH 063/200] ensure the layout is not L_DEFAULT anymore, even if last_split_layout is not initialized (Thanks eeemsi) --- src/con.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/con.c b/src/con.c index ccdd5682..cb4d0805 100644 --- a/src/con.c +++ b/src/con.c @@ -1040,7 +1040,7 @@ void con_set_layout(Con *con, int layout) { /* 2: Set the requested layout on the split container and mark it as * split. */ - new->layout = layout; + con_set_layout(new, layout); new->split = true; Con *old_focused = TAILQ_FIRST(&(con->focus_head)); @@ -1079,6 +1079,9 @@ void con_set_layout(Con *con, int layout) { * default", and in v4.9 we will remove it entirely (with an * appropriate i3-migrate-config mechanism). */ con->layout = con->last_split_layout; + /* In case last_split_layout was not initialized… */ + if (con->layout == L_DEFAULT) + con->layout = L_SPLITH; } else { /* We fill in last_split_layout when switching to a different layout * since there are many places in the code that don’t use From 2f22fae119bb119483298f5423ced3f4d6b37d84 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 12:46:37 +0200 Subject: [PATCH 064/200] i3-migrate-config-to-v4: use layout toggle split (Thanks eeemsi) --- i3-migrate-config-to-v4 | 2 +- testcases/t/171-config-migrate.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/i3-migrate-config-to-v4 b/i3-migrate-config-to-v4 index c8ff41c9..ae5bf4de 100755 --- a/i3-migrate-config-to-v4 +++ b/i3-migrate-config-to-v4 @@ -202,7 +202,7 @@ sub convert_command { # simple replacements my @replace = ( qr/^s/ => 'layout stacking', - qr/^d/ => 'layout default', + qr/^d/ => 'layout toggle split', qr/^T/ => 'layout tabbed', qr/^f($|[^go])/ => 'fullscreen', qr/^fg/ => 'fullscreen global', diff --git a/testcases/t/171-config-migrate.t b/testcases/t/171-config-migrate.t index e791bb01..dd0cbc60 100644 --- a/testcases/t/171-config-migrate.t +++ b/testcases/t/171-config-migrate.t @@ -195,7 +195,7 @@ EOT $output = migrate_config($input); ok(line_exists($output, qr|^bindsym Mod1\+s layout stacking$|), 's replaced'); -ok(line_exists($output, qr|^bindsym Mod1\+s layout default$|), 'd replaced'); +ok(line_exists($output, qr|^bindsym Mod1\+s layout toggle split$|), 'd replaced'); ok(line_exists($output, qr|^bindsym Mod1\+s layout tabbed$|), 'T replaced'); ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen$|), 'f replaced'); ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen global$|), 'fg replaced'); From 1d95d296a5f08ac024d56f20723f202158954549 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 16:31:57 +0200 Subject: [PATCH 065/200] parser: remove debug messages The new parser is proven by now (v4.2 was released 4 months ago), so removing these messages will make the logfile more readable. --- src/commands_parser.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/commands_parser.c b/src/commands_parser.c index 9af8e981..6cfac202 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -104,7 +104,6 @@ static void push_string(const char *identifier, char *str) { // XXX: ideally, this would be const char. need to check if that works with all // called functions. static char *get_string(const char *identifier) { - DLOG("Getting string %s from stack...\n", identifier); for (int c = 0; c < 10; c++) { if (stack[c].identifier == NULL) break; @@ -115,7 +114,6 @@ static char *get_string(const char *identifier) { } static void clear_stack(void) { - DLOG("clearing stack.\n"); for (int c = 0; c < 10; c++) { if (stack[c].str != NULL) free(stack[c].str); @@ -187,8 +185,6 @@ static struct CommandResult command_output; static void next_state(const cmdp_token *token) { if (token->next_state == __CALL) { - DLOG("should call stuff, yay. call_id = %d\n", - token->extra.call_identifier); subcommand_output.json_gen = command_output.json_gen; subcommand_output.needs_tree_render = false; GENERATED_call(token->extra.call_identifier, &subcommand_output); @@ -208,7 +204,7 @@ static void next_state(const cmdp_token *token) { /* TODO: Return parsing errors via JSON. */ struct CommandResult *parse_command(const char *input) { - DLOG("new parser handling: %s\n", input); + DLOG("COMMAND: *%s*\n", input); state = INITIAL; /* A YAJL JSON generator used for formatting replies. */ @@ -240,19 +236,14 @@ struct CommandResult *parse_command(const char *input) { *walk == '\r' || *walk == '\n') && *walk != '\0') walk++; - DLOG("remaining input = %s\n", walk); - cmdp_token_ptr *ptr = &(tokens[state]); token_handled = false; for (c = 0; c < ptr->n; c++) { token = &(ptr->array[c]); - DLOG("trying token %d = %s\n", c, token->name); /* A literal. */ if (token->name[0] == '\'') { - DLOG("literal\n"); if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) { - DLOG("found literal, moving to next state\n"); if (token->identifier != NULL) push_string(token->identifier, sstrdup(token->name + 1)); walk += strlen(token->name) - 1; @@ -265,7 +256,6 @@ struct CommandResult *parse_command(const char *input) { if (strcmp(token->name, "string") == 0 || strcmp(token->name, "word") == 0) { - DLOG("parsing this as a string\n"); const char *beginning = walk; /* Handle quoted strings (or words). */ if (*walk == '"') { @@ -310,7 +300,6 @@ struct CommandResult *parse_command(const char *input) { } if (token->identifier) push_string(token->identifier, str); - DLOG("str is \"%s\"\n", str); /* If we are at the end of a quoted string, skip the ending * double quote. */ if (*walk == '"') @@ -322,9 +311,7 @@ struct CommandResult *parse_command(const char *input) { } if (strcmp(token->name, "end") == 0) { - DLOG("checking for the end token.\n"); if (*walk == '\0' || *walk == ',' || *walk == ';') { - DLOG("yes, indeed. end\n"); next_state(token); token_handled = true; /* To make sure we start with an appropriate matching @@ -414,7 +401,6 @@ struct CommandResult *parse_command(const char *input) { y(array_close); - DLOG("command_output.needs_tree_render = %d\n", command_output.needs_tree_render); return &command_output; } From ac8b2f637ff00ebe2ee3500da25e77626f15ac55 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 4 Aug 2012 16:34:57 +0200 Subject: [PATCH 066/200] cleanup i3 sync protocol messages --- src/handlers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/handlers.c b/src/handlers.c index 048d3a3a..b8a7ccdf 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -624,10 +624,9 @@ static void handle_client_message(xcb_client_message_event_t *event) { tree_render(); x_push_changes(croot); } else if (event->type == A_I3_SYNC) { - DLOG("i3 sync, yay\n"); xcb_window_t window = event->data.data32[0]; uint32_t rnd = event->data.data32[1]; - DLOG("Sending random value %d back to X11 window 0x%08x\n", rnd, window); + DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window); void *reply = scalloc(32); xcb_client_message_event_t *ev = reply; From f80b877c6b7042f51f8340dde48ab6336039c8f3 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sat, 4 Aug 2012 23:31:38 +0200 Subject: [PATCH 067/200] man/i3bar: Reference j4status --- man/i3bar.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/i3bar.man b/man/i3bar.man index dcf3022b..e881899b 100644 --- a/man/i3bar.man +++ b/man/i3bar.man @@ -59,7 +59,7 @@ Instead, see the i3 documentation, especially the User’s Guide. == SEE ALSO -+i3status(1)+ or +conky(1)+ for programs generating a statusline. ++i3status(1)+, +j4status(1)+ or +conky(1)+ for programs generating a statusline. +dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar. From 78f5f2204d3b25ce0bac948eb7c0ed8d35808262 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 14:29:19 +0200 Subject: [PATCH 068/200] ipc: implement GET_VERSION to find out the i3 version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful for third-party scripts which require certain features and want to error out cleanly when they are run with an old i3 version. Additionally, i3 --version might be different from what’s actually running (an old version of the binary), so i3-msg -t get_version will be the best way to figure out the i3 version you are actually running from this commit on. --- common.mk | 9 ++++++++ docs/ipc | 34 +++++++++++++++++++++++++++++ i3-msg/main.c | 6 +++-- include/i3/ipc.h | 8 ++++++- man/i3-msg.man | 7 ++++-- src/ipc.c | 41 ++++++++++++++++++++++++++++++++++- testcases/t/193-ipc-version.t | 23 ++++++++++++++++++++ 7 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 testcases/t/193-ipc-version.t diff --git a/common.mk b/common.mk index f202166d..21e10257 100644 --- a/common.mk +++ b/common.mk @@ -23,6 +23,12 @@ else VERSION := ${I3_VERSION} endif +MAJOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 1) +MINOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 2) +PATCH_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 3) +ifeq (${PATCH_VERSION},) +PATCH_VERSION := 0 +endif ## Generic flags @@ -46,6 +52,9 @@ I3_CFLAGS += -Wunused-value I3_CFLAGS += -Iinclude I3_CPPFLAGS = -DI3_VERSION=\"${I3_VERSION}\" +I3_CPPFLAGS += -DMAJOR_VERSION=${MAJOR_VERSION} +I3_CPPFLAGS += -DMINOR_VERSION=${MINOR_VERSION} +I3_CPPFLAGS += -DPATCH_VERSION=${PATCH_VERSION} I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" diff --git a/docs/ipc b/docs/ipc index 525e9968..508e789c 100644 --- a/docs/ipc +++ b/docs/ipc @@ -70,6 +70,9 @@ 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. So, a typical message could look like this: -------------------------------------------------- @@ -125,6 +128,8 @@ MARKS (5):: Reply to the GET_MARKS message. BAR_CONFIG (6):: Reply to the GET_BAR_CONFIG message. +VERSION (7):: + Reply to the GET_VERSION message. === COMMAND reply @@ -534,6 +539,35 @@ urgent_workspace_text/urgent_workspace_bar:: } -------------- +=== Version reply + +The reply consists of a single JSON dictionary with the following keys: + +major (integer):: + The major version of i3, such as +4+. +minor (integer):: + The minor version of i3, such as +2+. Changes in the IPC interface (new + features) will only occur with new minor (or major) releases. However, + bugfixes might be introduced in patch releases, too. +patch (integer):: + The patch version of i3, such as +1+ (when the complete version is + +4.2.1+). +human_readable (string):: + A human-readable version of i3 containing the precise git version, + build date and branch name. When you need to display the i3 version to + your users, use the human-readable version whenever possible (since + this is what +i3 --version+ displays, too). + +*Example:* +------------------- +{ + "human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")", + "minor" : 2, + "patch" : 0, + "major" : 4 +} +------------------- + == Events [[events]] diff --git a/i3-msg/main.c b/i3-msg/main.c index ccf6e10f..a04e6690 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * i3-msg/main.c: Utility which sends messages to a running i3-instance using * IPC via UNIX domain sockets. @@ -73,9 +73,11 @@ int main(int argc, char *argv[]) { message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS; else if (strcasecmp(optarg, "get_bar_config") == 0) message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG; + else if (strcasecmp(optarg, "get_version") == 0) + message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION; else { printf("Unknown message type\n"); - printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config\n"); + printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_version\n"); exit(EXIT_FAILURE); } } else if (o == 'q') { diff --git a/include/i3/ipc.h b/include/i3/ipc.h index bfadf4cf..0906b7f9 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * This public header defines the different constants and message types to use * for the IPC interface to i3 (see docs/ipc for more information). @@ -40,6 +40,9 @@ /** Request the configuration for a specific 'bar' */ #define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6 +/** Request the i3 version */ +#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7 + /* * Messages from i3 to clients * @@ -66,6 +69,9 @@ /** Bar config reply type */ #define I3_IPC_REPLY_TYPE_BAR_CONFIG 6 +/** i3 version reply type */ +#define I3_IPC_REPLY_TYPE_VERSION 7 + /* * Events from i3 to clients. Events have the first bit set high. * diff --git a/man/i3-msg.man b/man/i3-msg.man index 891c6c28..6b548d36 100644 --- a/man/i3-msg.man +++ b/man/i3-msg.man @@ -1,7 +1,7 @@ i3-msg(1) ========= -Michael Stapelberg -v4.2, January 2012 +Michael Stapelberg +v4.2, August 2012 == NAME @@ -38,6 +38,9 @@ get_bar_config:: 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:: +Gets the version of i3. The reply will be a JSON-encoded dictionary with the +major, minor, patch and human-readable version. == DESCRIPTION diff --git a/src/ipc.c b/src/ipc.c index 1d19fc6c..77b8dbb3 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -536,6 +536,44 @@ IPC_HANDLER(get_marks) { y(free); } +/* + * Returns the version of i3 + * + */ +IPC_HANDLER(get_version) { +#if YAJL_MAJOR >= 2 + yajl_gen gen = yajl_gen_alloc(NULL); +#else + yajl_gen gen = yajl_gen_alloc(NULL, NULL); +#endif + y(map_open); + + ystr("major"); + y(integer, MAJOR_VERSION); + + ystr("minor"); + y(integer, MINOR_VERSION); + + ystr("patch"); + y(integer, PATCH_VERSION); + + ystr("human_readable"); + ystr(I3_VERSION); + + y(map_close); + + const unsigned char *payload; +#if YAJL_MAJOR >= 2 + size_t length; +#else + unsigned int length; +#endif + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_VERSION, payload); + y(free); +} + /* * Formats the reply message for a GET_BAR_CONFIG request and sends it to the * client. @@ -792,7 +830,7 @@ IPC_HANDLER(subscribe) { /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[7] = { +handler_t handlers[8] = { handle_command, handle_get_workspaces, handle_subscribe, @@ -800,6 +838,7 @@ handler_t handlers[7] = { handle_tree, handle_get_marks, handle_get_bar_config, + handle_get_version, }; /* diff --git a/testcases/t/193-ipc-version.t b/testcases/t/193-ipc-version.t new file mode 100644 index 00000000..172dd8f0 --- /dev/null +++ b/testcases/t/193-ipc-version.t @@ -0,0 +1,23 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verifies that we can get the version number of i3 via IPC. +use i3test; + +my $i3 = i3(get_socket_path()); +$i3->connect->recv; +# We explicitly send the version message because AnyEvent::I3’s 'version' sugar +# method has a fallback which tries to parse the version number from i3 +# --version for older versions, and we want to avoid using that. +my $version = $i3->message(7, "")->recv; + +# We need to change this when the major version changes (but we need to touch a +# lot of changes then anyways). +is($version->{major}, 4, 'major version is 4'); + +cmp_ok($version->{minor}, '>', 0, 'minor version > 0'); + +is(int($version->{minor}), $version->{minor}, 'minor version is an integer'); +is(int($version->{patch}), $version->{patch}, 'patch version is an integer'); +like($version->{human_readable}, qr/branch/, 'human readable version contains branch name'); + +done_testing; From acdd5287a9223d2eb5f47fa519f6d21232eabcc7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 14:38:52 +0200 Subject: [PATCH 069/200] unbreak the build --- Makefile | 3 ++- common.mk | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f1104ead..16b444c4 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,8 @@ dist: distclean # Only copy source code from i3-input mkdir i3-${VERSION}/i3-input find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; - echo -n ${I3_VERSION} > i3-${VERSION}/VERSION + echo -n ${I3_VERSION} > i3-${VERSION}/I3_VERSION + echo -n ${VERSION} > i3-${VERSION}/VERSION # Pre-generate a manpage to allow distributors to skip this step and save some dependencies $(MAKE) mans cp man/*.1 i3-${VERSION}/man/ diff --git a/common.mk b/common.mk index 21e10257..0d2ad51e 100644 --- a/common.mk +++ b/common.mk @@ -15,12 +15,12 @@ ifndef SYSCONFDIR endif endif -I3_VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)' +# In dist tarballs, the version is stored in the I3_VERSION and VERSION files. +I3_VERSION := '$(shell [ -f $(TOPDIR)/I3_VERSION ] && cat $(TOPDIR)/I3_VERSION)' +VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)' ifeq ('',$(I3_VERSION)) VERSION := $(shell git describe --tags --abbrev=0) I3_VERSION := '$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch \"$(shell git describe --tags --always --all | sed s:heads/::)\")' -else -VERSION := ${I3_VERSION} endif MAJOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 1) From 0e47d52c437f71f1956022635e6766dda7320e62 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 14:39:45 +0200 Subject: [PATCH 070/200] ipc: clarify the patch version for sth like 4.2 --- docs/ipc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ipc b/docs/ipc index 508e789c..0a1f9cfe 100644 --- a/docs/ipc +++ b/docs/ipc @@ -551,7 +551,7 @@ minor (integer):: bugfixes might be introduced in patch releases, too. patch (integer):: The patch version of i3, such as +1+ (when the complete version is - +4.2.1+). + +4.2.1+). For versions such as +4.2+, patch will be set to +0+. human_readable (string):: A human-readable version of i3 containing the precise git version, build date and branch name. When you need to display the i3 version to From d5b7146123a95f29ad67e8afae157677fcd4ead5 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 14:42:12 +0200 Subject: [PATCH 071/200] docs/ipc: make the reply sections consistent (they contain the reply type) --- docs/ipc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ipc b/docs/ipc index 0a1f9cfe..f8dfa78e 100644 --- a/docs/ipc +++ b/docs/ipc @@ -211,7 +211,7 @@ default) or whether a JSON parse error occurred. { "success": true } ------------------- -=== GET_OUTPUTS reply +=== OUTPUTS reply The reply consists of a serialized list of outputs. Each output has the following properties: @@ -539,7 +539,7 @@ urgent_workspace_text/urgent_workspace_bar:: } -------------- -=== Version reply +=== VERSION reply The reply consists of a single JSON dictionary with the following keys: From 991a9de0d7285fde26d354134068a2d30988dab7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 15:59:08 +0200 Subject: [PATCH 072/200] log.h: tell the compiler these logging functions use printf-like formats This gives us additional warnings and is supported in gcc and clang. --- include/log.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/log.h b/include/log.h index 11555ab7..d3e967e7 100644 --- a/include/log.h +++ b/include/log.h @@ -49,20 +49,23 @@ void set_verbosity(bool _verbose); * but only if debug logging was activated. * */ -void debuglog(char *fmt, ...); +void debuglog(char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); /** * Logs the given message to stdout while prefixing the current time to it. * */ -void errorlog(char *fmt, ...); +void errorlog(char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); /** * Logs the given message to stdout while prefixing the current time to it, * but only if verbose mode is activated. * */ -void verboselog(char *fmt, ...); +void verboselog(char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); /** * Logs the given message to stdout while prefixing the current time to it. From cab8e3c46f825b5c7ff9f7008987b11e3163c83a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 15:59:45 +0200 Subject: [PATCH 073/200] log.h: include left-over slog() prototype --- include/log.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/log.h b/include/log.h index d3e967e7..0ad0fb37 100644 --- a/include/log.h +++ b/include/log.h @@ -67,11 +67,4 @@ void errorlog(char *fmt, ...) void verboselog(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -/** - * Logs the given message to stdout while prefixing the current time to it. - * This is to be called by LOG() which includes filename/linenumber - * - */ -void slog(char *fmt, va_list args); - #endif From 8a1c8115cad58586527b8461118917dc25653cd4 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 16:34:38 +0200 Subject: [PATCH 074/200] fix a few warnings/places where the clang static analyzer complains --- i3-input/main.c | 10 ---------- i3-nagbar/main.c | 10 ++++++---- src/commands.c | 8 ++++++-- src/con.c | 3 +++ src/floating.c | 2 +- src/ipc.c | 2 +- src/key_press.c | 2 +- src/load_layout.c | 2 +- src/main.c | 3 +-- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index b5709523..7e236ca7 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -126,16 +126,6 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) { printf("releasing %d, state raw = %d\n", event->detail, event->state); - /* See the documentation of xcb_key_symbols_get_keysym for this one. - * Basically: We get either col 0 or col 1, depending on whether shift is - * pressed. */ - int col = (event->state & XCB_MOD_MASK_SHIFT); - - /* If modeswitch is currently active, we need to look in group 2 or 3, - * respectively. */ - if (modeswitch_active) - col += 2; - xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state); if (sym == XK_Mode_switch) { printf("Mode switch disabled\n"); diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 1dbd7736..87214477 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -216,7 +216,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { } int main(int argc, char *argv[]) { - char *pattern = strdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"); + char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"); int o, option_index = 0; enum { TYPE_ERROR = 0, TYPE_WARNING = 1 } bar_type = TYPE_ERROR; @@ -232,7 +232,7 @@ int main(int argc, char *argv[]) { char *options_string = "b:f:m:t:vh"; - prompt = strdup("Please do not run this program."); + prompt = sstrdup("Please do not run this program."); while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { switch (o) { @@ -241,11 +241,11 @@ int main(int argc, char *argv[]) { return 0; case 'f': FREE(pattern); - pattern = strdup(optarg); + pattern = sstrdup(optarg); break; case 'm': FREE(prompt); - prompt = strdup(optarg); + prompt = sstrdup(optarg); break; case 't': bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR); @@ -432,5 +432,7 @@ int main(int argc, char *argv[]) { free(event); } + FREE(pattern); + return 0; } diff --git a/src/commands.c b/src/commands.c index 44e6a23d..964dfe29 100644 --- a/src/commands.c +++ b/src/commands.c @@ -452,7 +452,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { return; } - LOG("should move window to workspace with number %d\n", which); + LOG("should move window to workspace %s\n", which); /* get the workspace */ Con *output, *workspace = NULL; @@ -835,7 +835,7 @@ void cmd_workspace_number(I3_CMD, char *which) { child->num == parsed_num); if (!workspace) { - LOG("There is no workspace with number %d, creating a new one.\n", parsed_num); + LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num); ysuccess(true); /* terminate the which string after the endposition of the number */ *endptr = '\0'; @@ -1421,6 +1421,10 @@ void cmd_layout(I3_CMD, char *layout_str) { layout = L_SPLITV; else if (strcmp(layout_str, "splith") == 0) layout = L_SPLITH; + else { + ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str); + return; + } DLOG("changing layout to %s (%d)\n", layout_str, layout); diff --git a/src/con.c b/src/con.c index cb4d0805..01305f1a 100644 --- a/src/con.c +++ b/src/con.c @@ -1260,6 +1260,9 @@ bool con_fullscreen_permits_focusing(Con *con) { while (fs && fs->fullscreen_mode == CF_NONE) fs = fs->parent; + /* fs must be non-NULL since the workspace con doesn’t have CF_NONE and + * there always has to be a workspace con in the hierarchy. */ + assert(fs != NULL); /* The most common case is we hit the workspace level. In this * situation, changing focus is also harmless. */ assert(fs->fullscreen_mode != CF_NONE); diff --git a/src/floating.c b/src/floating.c index 0bc5f6d6..16aab992 100644 --- a/src/floating.c +++ b/src/floating.c @@ -493,7 +493,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to, border_t border, callback_t callback, const void *extra) { uint32_t new_x, new_y; - Rect old_rect; + Rect old_rect = { 0, 0, 0, 0 }; if (con != NULL) memcpy(&old_rect, &(con->rect), sizeof(Rect)); diff --git a/src/ipc.c b/src/ipc.c index 77b8dbb3..642b6b97 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -758,7 +758,7 @@ static int add_subscription(void *extra, const unsigned char *s, #endif ipc_client *client = extra; - DLOG("should add subscription to extra %p, sub %.*s\n", client, len, s); + DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s); int event = client->num_events; client->num_events++; diff --git a/src/key_press.c b/src/key_press.c index 9aaea8f9..68f3865a 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -291,7 +291,7 @@ void handle_key_press(xcb_key_press_event_t *event) { command_failed = false; yajl_status state = yajl_parse(handle, reply, length); if (state != yajl_status_ok) { - ELOG("Could not parse my own reply. That's weird. reply is %.*s\n", length, reply); + ELOG("Could not parse my own reply. That's weird. reply is %.*s\n", (int)length, reply); } else { if (command_failed) start_commanderror_nagbar(); diff --git a/src/load_layout.c b/src/load_layout.c index 54735d91..8596dfc4 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -139,7 +139,7 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) { #else static int json_string(void *ctx, const unsigned char *val, unsigned int len) { #endif - LOG("string: %.*s for key %s\n", len, val, last_key); + LOG("string: %.*s for key %s\n", (int)len, val, last_key); if (parsing_swallows) { /* TODO: the other swallowing keys */ if (strcasecmp(last_key, "class") == 0) { diff --git a/src/main.c b/src/main.c index b8e2e08c..aaa58171 100644 --- a/src/main.c +++ b/src/main.c @@ -424,7 +424,7 @@ int main(int argc, char *argv[]) { } optind++; } - LOG("Command is: %s (%d bytes)\n", payload, strlen(payload)); + DLOG("Command is: %s (%zd bytes)\n", payload, strlen(payload)); char *socket_path = root_atom_contents("I3_SOCKET_PATH"); if (!socket_path) { ELOG("Could not get i3 IPC socket path\n"); @@ -660,7 +660,6 @@ int main(int argc, char *argv[]) { Output *output = NULL; if (!(pointerreply = xcb_query_pointer_reply(conn, pointercookie, NULL))) { ELOG("Could not query pointer position, using first screen\n"); - output = get_first_output(); } else { DLOG("Pointer at %d, %d\n", pointerreply->root_x, pointerreply->root_y); output = get_output_containing(pointerreply->root_x, pointerreply->root_y); From 952e5b6059a87cf3c32a62edef5e91730ce380b4 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 16:35:54 +0200 Subject: [PATCH 075/200] pre-compile all.h, saves quite some build time before: $ time CC=clang make -j16 CC=clang make -j16 6,04s user 0,86s system 468% cpu 1,471 total CC=clang make -j16 6,05s user 0,87s system 468% cpu 1,477 total CC=clang make -j16 6,15s user 0,86s system 464% cpu 1,510 total CC=clang make -j16 6,05s user 0,93s system 467% cpu 1,493 total CC=clang make -j16 6,10s user 0,84s system 461% cpu 1,507 total $ time CC=gcc make -j16 CC=gcc make -j16 9,91s user 1,43s system 508% cpu 2,231 total CC=gcc make -j16 10,02s user 1,37s system 500% cpu 2,275 total CC=gcc make -j16 9,80s user 1,60s system 507% cpu 2,245 total CC=gcc make -j16 10,02s user 1,44s system 506% cpu 2,264 total CC=gcc make -j16 9,99s user 1,45s system 505% cpu 2,261 total after: $ time CC=clang make -j16 CC=clang make -j16 3,41s user 0,83s system 375% cpu 1,131 total CC=clang make -j16 3,29s user 0,90s system 373% cpu 1,122 total CC=clang make -j16 3,35s user 0,77s system 369% cpu 1,116 total CC=clang make -j16 3,36s user 0,78s system 374% cpu 1,105 total CC=clang make -j16 3,46s user 0,75s system 373% cpu 1,126 total $ time CC=gcc make -j16 CC=gcc make -j16 10,74s user 1,44s system 494% cpu 2,462 total CC=gcc make -j16 10,68s user 1,54s system 497% cpu 2,453 total CC=gcc make -j16 10,60s user 1,60s system 488% cpu 2,499 total CC=gcc make -j16 10,63s user 1,51s system 485% cpu 2,502 total CC=gcc make -j16 10,70s user 1,51s system 497% cpu 2,453 total Therefore, we enable pre-compiled headers only when CC=clang. --- src/i3.mk | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/i3.mk b/src/i3.mk index 6030bbc6..8c85e31d 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -9,24 +9,38 @@ i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/ i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +# When using clang, we use pre-compiled headers to speed up the build. With +# gcc, this actually makes the build slower. +ifeq ($(CC),clang) +i3_HEADERS_DEP := $(i3_HEADERS) include/all.h.pch +PCH_FLAGS := -include include/all.h +else +i3_HEADERS_DEP := $(i3_HEADERS) +PCH_FLAGS := +endif + i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) -src/%.o: src/%.c $(i3_HEADERS) - echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< +include/all.h.pch: $(i3_HEADERS) + echo "[i3] PCH all.h" + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch -src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS) +src/%.o: src/%.c $(i3_HEADERS_DEP) + echo "[i3] CC $<" + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ $< + +src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS_DEP) echo "[i3] LEX $<" $(FLEX) -i -o $@ $< -src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS) +src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS_DEP) echo "[i3] YACC $<" $(BISON) --debug --verbose -b $(basename $< .y) -d $< # This target compiles the command parser twice: # Once with -DTEST_PARSER, creating a stand-alone executable used for tests, # and once as an object file for i3. -src/commands_parser.o: src/commands_parser.c $(i3_HEADERS) i3-command-parser.stamp +src/commands_parser.o: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp echo "[i3] CC $<" $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< @@ -60,4 +74,4 @@ install-i3: i3 clean-i3: echo "[i3] Clean" - rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} + rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} From 48f1e383ca2322ffbfbc66cd7c0325b97f8d36a9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 17:00:24 +0200 Subject: [PATCH 076/200] makefile: canonicalize path when compiling Compilers store the path with which they were called in the debug symbols. Therefore, this will make backtraces show something like ../i3-4.2/src/main.c instead of src/main.c. See also http://stackoverflow.com/questions/6473561/ --- src/i3.mk | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/i3.mk b/src/i3.mk index 8c85e31d..f997c821 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -21,21 +21,29 @@ endif i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) +# The basename/readlink calls are for canonicalizing the path: Instead +# of src/main.c, we will see something like ../i3-4.2/src/main.c in +# debugger backtraces, making it clearer which code belongs to i3 and +# which code doesn’t. +# We only do this for src/ since all the other subdirectories contain i3 in +# their name already. +canonical_path := ../$(shell basename $(shell readlink -f .)) + include/all.h.pch: $(i3_HEADERS) echo "[i3] PCH all.h" $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch src/%.o: src/%.c $(i3_HEADERS_DEP) echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$< src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS_DEP) echo "[i3] LEX $<" - $(FLEX) -i -o $@ $< + $(FLEX) -i -o $@ ${canonical_path}/$< src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS_DEP) echo "[i3] YACC $<" - $(BISON) --debug --verbose -b $(basename $< .y) -d $< + $(BISON) --debug --verbose -b $(basename $< .y) -d ${canonical_path}/$< # This target compiles the command parser twice: # Once with -DTEST_PARSER, creating a stand-alone executable used for tests, @@ -43,7 +51,7 @@ src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS_DEP) src/commands_parser.o: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp echo "[i3] CC $<" $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec echo "[i3] Generating command parser" From f27735f620aaaf379199af4cadaf4e85b58616e5 Mon Sep 17 00:00:00 2001 From: Iakov Davydov Date: Sun, 22 Jul 2012 13:57:07 +0400 Subject: [PATCH 077/200] create hide_edge_borders option --- docs/userguide | 16 ++++++++++++++++ include/con.h | 6 ++++++ include/config.h | 5 +++++ include/data.h | 5 +++++ src/cfgparse.l | 1 + src/cfgparse.y | 10 ++++++++++ src/con.c | 38 ++++++++++++++++++++++++++++++++++++-- src/x.c | 19 +++++++++++++------ 8 files changed, 92 insertions(+), 8 deletions(-) diff --git a/docs/userguide b/docs/userguide index 64063831..46a19e93 100644 --- a/docs/userguide +++ b/docs/userguide @@ -468,6 +468,22 @@ new_window new_window 1pixel --------------------- +=== Hiding vertical borders + +You can hide vertical borders adjacent to the screen edges using ++hide_edge_borders+. This is useful if you are using scrollbars. This option is +disabled by default. + +*Syntax*: +---------------------------- +hide_edge_borders +---------------------------- + +*Example*: +---------------------- +hide_edge_borders yes +---------------------- + === Arbitrary commands for specific windows (for_window) With the +for_window+ command, you can let i3 execute any command when it diff --git a/include/con.h b/include/con.h index 1965da7c..20e83df9 100644 --- a/include/con.h +++ b/include/con.h @@ -221,6 +221,12 @@ Con *con_descend_direction(Con *con, direction_t direction); */ Rect con_border_style_rect(Con *con); +/** + * Returns adjacent borders of the window. We need this if hide_edge_borders is + * enabled. + */ +adjacent_t con_adjacent_borders(Con *con); + /** * Use this function to get a container’s border style. This is important * because when inside a stack, the border style is always BS_NORMAL. diff --git a/include/config.h b/include/config.h index 310f8b02..b6f356ad 100644 --- a/include/config.h +++ b/include/config.h @@ -108,6 +108,11 @@ struct Config { * It is not planned to add any different focus models. */ bool disable_focus_follows_mouse; + /** Remove vertical borders if they are adjacent to the screen edge. + * This is useful if you are reaching scrollbar on the edge of the + * screen. By default, this is disabled. */ + bool hide_edge_borders; + /** By default, a workspace bar is drawn at the bottom of the screen. * If you want to have a more fancy bar, it is recommended to replace * the whole bar by dzen2, for example using the i3-wsbar script which diff --git a/include/data.h b/include/data.h index d28f1756..acce59be 100644 --- a/include/data.h +++ b/include/data.h @@ -60,6 +60,11 @@ typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_1PIXEL = 2 } border_style_t; * only this specific window or the whole X11 client */ typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t; +/** describes if the window is adjacent to the output (physical screen) edges. */ +typedef enum { ADJ_NONE = 0, + ADJ_LEFT_SCREEN_EDGE = 1, + ADJ_RIGHT_SCREEN_EDGE = 2} adjacent_t; + enum { BIND_NONE = 0, BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */ diff --git a/src/cfgparse.l b/src/cfgparse.l index cdf110d3..e2705b17 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -200,6 +200,7 @@ new_float { return TOKNEWFLOAT; } normal { return TOK_NORMAL; } none { return TOK_NONE; } 1pixel { return TOK_1PIXEL; } +hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; } focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; } force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; } force_xinerama { return TOK_FORCE_XINERAMA; } diff --git a/src/cfgparse.y b/src/cfgparse.y index 68a51996..a2f1b9f4 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -735,6 +735,7 @@ void parse_file(const char *f) { %token TOK_NORMAL "normal" %token TOK_NONE "none" %token TOK_1PIXEL "1pixel" +%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders" %token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse" %token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping" %token TOK_FORCE_XINERAMA "force_xinerama" @@ -833,6 +834,7 @@ line: | workspace_layout | new_window | new_float + | hide_edge_borders | focus_follows_mouse | force_focus_wrapping | force_xinerama @@ -1474,6 +1476,14 @@ bool: } ; +hide_edge_borders: + TOK_HIDE_EDGE_BORDERS bool + { + DLOG("hide edge borders = %d\n", $2); + config.hide_edge_borders = $2; + } + ; + focus_follows_mouse: TOKFOCUSFOLLOWSMOUSE bool { diff --git a/src/con.c b/src/con.c index 01305f1a..4cddf227 100644 --- a/src/con.c +++ b/src/con.c @@ -934,12 +934,32 @@ Con *con_descend_direction(Con *con, direction_t direction) { * */ Rect con_border_style_rect(Con *con) { + adjacent_t adjacent_to = ADJ_NONE; + Rect result; + if (config.hide_edge_borders) + adjacent_to = con_adjacent_borders(con); switch (con_border_style(con)) { case BS_NORMAL: - return (Rect){2, 0, -(2 * 2), -2}; + result = (Rect){2, 0, -(2 * 2), -2}; + if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) { + result.x -= 2; + result.width += 2; + } + if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) { + result.width += 2; + } + return result; case BS_1PIXEL: - return (Rect){1, 1, -2, -2}; + result = (Rect){1, 1, -2, -2}; + if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) { + result.x -= 1; + result.width += 1; + } + if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) { + result.width += 1; + } + return result; case BS_NONE: return (Rect){0, 0, 0, 0}; @@ -949,6 +969,20 @@ Rect con_border_style_rect(Con *con) { } } +/* + * Returns adjacent borders of the window. We need this if hide_edge_borders is + * enabled. + */ +adjacent_t con_adjacent_borders(Con *con) { + adjacent_t result = ADJ_NONE; + Con *output = con_get_output(con); + if (con->rect.x == output->rect.x) + result |= ADJ_LEFT_SCREEN_EDGE; + if (con->rect.x + con->rect.width == output->rect.x + output->rect.width) + result |= ADJ_RIGHT_SCREEN_EDGE; + return result; +} + /* * Use this function to get a container’s border style. This is important * because when inside a stack, the border style is always BS_NORMAL. diff --git a/src/x.c b/src/x.c index 08eb8fee..f4d1416f 100644 --- a/src/x.c +++ b/src/x.c @@ -299,6 +299,9 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) { void x_draw_decoration(Con *con) { Con *parent = con->parent; bool leaf = con_is_leaf(con); + adjacent_t adjacent_to = ADJ_NONE; + if (config.hide_edge_borders) + adjacent_to = con_adjacent_borders(con); /* This code needs to run for: * • leaf containers * • non-leaf containers which are in a stacked/tabbed container @@ -408,12 +411,16 @@ void x_draw_decoration(Con *con) { * rectangle because some childs are not freely resizable and we want * their background color to "shine through". */ xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background }); - xcb_rectangle_t borders[] = { - { 0, 0, br.x, r->height }, - { 0, r->height + br.height + br.y, r->width, r->height }, - { r->width + br.width + br.x, 0, r->width, r->height } - }; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 3, borders); + if (!(adjacent_to & ADJ_LEFT_SCREEN_EDGE)) { + xcb_rectangle_t leftline = { 0, 0, br.x, r->height }; + xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline); + } + if (!(adjacent_to & ADJ_RIGHT_SCREEN_EDGE)) { + xcb_rectangle_t rightline = { r->width + br.width + br.x, 0, r->width, r->height }; + xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline); + } + xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height }; + xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); /* 1pixel border needs an additional line at the top */ if (p->border_style == BS_1PIXEL) { xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y }; From afc16953b96bcf3cb6794daaf376eeaef1a2a720 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 20:52:42 +0200 Subject: [PATCH 078/200] correctly store last_split_layout when we are dealing with a workspace-level layout change (Thanks aksr) fixes #763 --- src/con.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/con.c b/src/con.c index 4cddf227..35ad540c 100644 --- a/src/con.c +++ b/src/con.c @@ -1062,6 +1062,12 @@ void con_set_border_style(Con *con, int border_style) { * */ void con_set_layout(Con *con, int layout) { + /* We fill in last_split_layout when switching to a different layout + * since there are many places in the code that don’t use + * con_set_layout(). */ + if (con->layout == L_SPLITH || con->layout == L_SPLITV) + con->last_split_layout = con->layout; + /* When the container type is CT_WORKSPACE, the user wants to change the * whole workspace into stacked/tabbed mode. To do this and still allow * intuitive operations (like level-up and then opening a new window), we @@ -1075,6 +1081,7 @@ void con_set_layout(Con *con, int layout) { /* 2: Set the requested layout on the split container and mark it as * split. */ con_set_layout(new, layout); + new->last_split_layout = con->last_split_layout; new->split = true; Con *old_focused = TAILQ_FIRST(&(con->focus_head)); @@ -1117,11 +1124,6 @@ void con_set_layout(Con *con, int layout) { if (con->layout == L_DEFAULT) con->layout = L_SPLITH; } else { - /* We fill in last_split_layout when switching to a different layout - * since there are many places in the code that don’t use - * con_set_layout(). */ - if (con->layout == L_SPLITH || con->layout == L_SPLITV) - con->last_split_layout = con->layout; con->layout = layout; } } From 1b2d2224491ee92c06f51e46062ec0f489f09d35 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 5 Aug 2012 20:56:33 +0200 Subject: [PATCH 079/200] Properly report errors in 'focus parent' (Thanks eeemsi) Also, make X11 errors debug log level only. They are harmless usually. fixes #762 --- src/commands.c | 2 +- src/main.c | 2 +- src/tree.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands.c b/src/commands.c index 964dfe29..b730bc44 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1263,7 +1263,7 @@ void cmd_focus_level(I3_CMD, char *level) { if (con_fullscreen_permits_focusing(focused->parent)) success = level_up(); else - LOG("Currently in fullscreen, not going up\n"); + ELOG("'focus parent': Currently in fullscreen, not going up\n"); } } diff --git a/src/main.c b/src/main.c index aaa58171..2c3dc7e9 100644 --- a/src/main.c +++ b/src/main.c @@ -121,7 +121,7 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { DLOG("Expected X11 Error received for sequence %x\n", event->sequence); else { xcb_generic_error_t *error = (xcb_generic_error_t*)event; - ELOG("X11 Error received! sequence 0x%x, error_code = %d\n", + DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n", error->sequence, error->error_code); } free(event); diff --git a/src/tree.c b/src/tree.c index 068b0570..650c9a54 100644 --- a/src/tree.c +++ b/src/tree.c @@ -389,7 +389,7 @@ bool level_up(void) { if ((focused->parent->type != CT_CON && focused->parent->type != CT_WORKSPACE) || focused->type == CT_WORKSPACE) { - LOG("Cannot go up any further\n"); + ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n"); return false; } con_focus(focused->parent); From 57effd65b29c5e4215b0408f416df0f5683a8ea4 Mon Sep 17 00:00:00 2001 From: Axel Wagner Date: Sun, 5 Aug 2012 21:41:36 +0200 Subject: [PATCH 080/200] Make horizontal edge-borders hidable too --- docs/userguide | 8 ++++---- include/config.h | 7 ++++--- include/data.h | 6 ++++-- src/cfgparse.l | 1 + src/cfgparse.y | 13 ++++++++++++- src/con.c | 34 ++++++++++++++++++++++++---------- src/x.c | 22 ++++++++++++++-------- 7 files changed, 63 insertions(+), 28 deletions(-) diff --git a/docs/userguide b/docs/userguide index 46a19e93..37e55d46 100644 --- a/docs/userguide +++ b/docs/userguide @@ -471,17 +471,17 @@ new_window 1pixel === Hiding vertical borders You can hide vertical borders adjacent to the screen edges using -+hide_edge_borders+. This is useful if you are using scrollbars. This option is -disabled by default. ++hide_edge_borders+. This is useful if you are using scrollbars, or do not want +to waste even two pixels in displayspace. Default is none. *Syntax*: ---------------------------- -hide_edge_borders +hide_edge_borders ---------------------------- *Example*: ---------------------- -hide_edge_borders yes +hide_edge_borders vertical ---------------------- === Arbitrary commands for specific windows (for_window) diff --git a/include/config.h b/include/config.h index b6f356ad..ebb24864 100644 --- a/include/config.h +++ b/include/config.h @@ -108,10 +108,11 @@ struct Config { * It is not planned to add any different focus models. */ bool disable_focus_follows_mouse; - /** Remove vertical borders if they are adjacent to the screen edge. + /** Remove borders if they are adjacent to the screen edge. * This is useful if you are reaching scrollbar on the edge of the - * screen. By default, this is disabled. */ - bool hide_edge_borders; + * screen or do not want to waste a single pixel of displayspace. + * By default, this is disabled. */ + adjacent_t hide_edge_borders; /** By default, a workspace bar is drawn at the bottom of the screen. * If you want to have a more fancy bar, it is recommended to replace diff --git a/include/data.h b/include/data.h index acce59be..a5ac943c 100644 --- a/include/data.h +++ b/include/data.h @@ -62,8 +62,10 @@ typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_win /** describes if the window is adjacent to the output (physical screen) edges. */ typedef enum { ADJ_NONE = 0, - ADJ_LEFT_SCREEN_EDGE = 1, - ADJ_RIGHT_SCREEN_EDGE = 2} adjacent_t; + ADJ_LEFT_SCREEN_EDGE = (1 << 0), + ADJ_RIGHT_SCREEN_EDGE = (1 << 1), + ADJ_UPPER_SCREEN_EDGE = (1 << 2), + ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t; enum { BIND_NONE = 0, diff --git a/src/cfgparse.l b/src/cfgparse.l index e2705b17..04117624 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -201,6 +201,7 @@ normal { return TOK_NORMAL; } none { return TOK_NONE; } 1pixel { return TOK_1PIXEL; } hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; } +both { return TOK_BOTH; } focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; } force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; } force_xinerama { return TOK_FORCE_XINERAMA; } diff --git a/src/cfgparse.y b/src/cfgparse.y index a2f1b9f4..af7d77cf 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -736,6 +736,7 @@ void parse_file(const char *f) { %token TOK_NONE "none" %token TOK_1PIXEL "1pixel" %token TOK_HIDE_EDGE_BORDERS "hide_edge_borders" +%token TOK_BOTH "both" %token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse" %token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping" %token TOK_FORCE_XINERAMA "force_xinerama" @@ -800,6 +801,8 @@ void parse_file(const char *f) { %type layout_mode %type border_style %type new_window +%type hide_edge_borders +%type edge_hiding_mode %type new_float %type colorpixel %type bool @@ -1477,13 +1480,21 @@ bool: ; hide_edge_borders: - TOK_HIDE_EDGE_BORDERS bool + TOK_HIDE_EDGE_BORDERS edge_hiding_mode { DLOG("hide edge borders = %d\n", $2); config.hide_edge_borders = $2; } ; +edge_hiding_mode: + TOK_NONE { $$ = ADJ_NONE; } + | TOK_VERT { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; } + | TOK_HORIZ { $$ = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; } + | TOK_BOTH { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; } + | bool { $$ = ($1 ? ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE : ADJ_NONE); } + ; + focus_follows_mouse: TOKFOCUSFOLLOWSMOUSE bool { diff --git a/src/con.c b/src/con.c index 35ad540c..6f2a85b2 100644 --- a/src/con.c +++ b/src/con.c @@ -934,31 +934,41 @@ Con *con_descend_direction(Con *con, direction_t direction) { * */ Rect con_border_style_rect(Con *con) { - adjacent_t adjacent_to = ADJ_NONE; + adjacent_t borders_to_hide = ADJ_NONE; Rect result; - if (config.hide_edge_borders) - adjacent_to = con_adjacent_borders(con); + borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; switch (con_border_style(con)) { case BS_NORMAL: result = (Rect){2, 0, -(2 * 2), -2}; - if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) { + if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { result.x -= 2; result.width += 2; } - if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) { + if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) { result.width += 2; } + /* With normal borders we never hide the upper border */ + if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) { + result.height += 2; + } return result; case BS_1PIXEL: result = (Rect){1, 1, -2, -2}; - if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) { + if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { result.x -= 1; result.width += 1; } - if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) { + if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) { result.width += 1; } + if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE) { + result.y -= 1; + result.height += 1; + } + if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) { + result.height += 1; + } return result; case BS_NONE: @@ -975,11 +985,15 @@ Rect con_border_style_rect(Con *con) { */ adjacent_t con_adjacent_borders(Con *con) { adjacent_t result = ADJ_NONE; - Con *output = con_get_output(con); - if (con->rect.x == output->rect.x) + Con *workspace = con_get_workspace(con); + if (con->rect.x == workspace->rect.x) result |= ADJ_LEFT_SCREEN_EDGE; - if (con->rect.x + con->rect.width == output->rect.x + output->rect.width) + if (con->rect.x + con->rect.width == workspace->rect.x + workspace->rect.width) result |= ADJ_RIGHT_SCREEN_EDGE; + if (con->rect.y == workspace->rect.y) + result |= ADJ_UPPER_SCREEN_EDGE; + if (con->rect.y + con->rect.height == workspace->rect.y + workspace->rect.height) + result |= ADJ_LOWER_SCREEN_EDGE; return result; } diff --git a/src/x.c b/src/x.c index f4d1416f..cd85bcc6 100644 --- a/src/x.c +++ b/src/x.c @@ -299,19 +299,19 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) { void x_draw_decoration(Con *con) { Con *parent = con->parent; bool leaf = con_is_leaf(con); - adjacent_t adjacent_to = ADJ_NONE; - if (config.hide_edge_borders) - adjacent_to = con_adjacent_borders(con); + /* This code needs to run for: * • leaf containers * • non-leaf containers which are in a stacked/tabbed container * * It does not need to run for: + * • direct children of outputs * • floating containers (they don’t have a decoration) */ if ((!leaf && parent->layout != L_STACKED && parent->layout != L_TABBED) || + parent->type == CT_OUTPUT || con->type == CT_FLOATING_CON) return; @@ -399,6 +399,10 @@ void x_draw_decoration(Con *con) { /* 3: draw a rectangle in border color around the client */ if (p->border_style != BS_NONE && p->con_is_leaf) { + /* We might hide some borders adjacent to the screen-edge */ + adjacent_t borders_to_hide = ADJ_NONE; + borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; + Rect br = con_border_style_rect(con); #if 0 DLOG("con->rect spans %d x %d\n", con->rect.width, con->rect.height); @@ -411,18 +415,20 @@ void x_draw_decoration(Con *con) { * rectangle because some childs are not freely resizable and we want * their background color to "shine through". */ xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background }); - if (!(adjacent_to & ADJ_LEFT_SCREEN_EDGE)) { + if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) { xcb_rectangle_t leftline = { 0, 0, br.x, r->height }; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline); } - if (!(adjacent_to & ADJ_RIGHT_SCREEN_EDGE)) { + if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) { xcb_rectangle_t rightline = { r->width + br.width + br.x, 0, r->width, r->height }; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline); } - xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height }; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); + if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) { + xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height }; + xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); + } /* 1pixel border needs an additional line at the top */ - if (p->border_style == BS_1PIXEL) { + if (p->border_style == BS_1PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y }; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline); } From 506b7f40048185bd3e7a87b9ec27e58c6724046c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 6 Aug 2012 03:11:11 +0200 Subject: [PATCH 081/200] =?UTF-8?q?Bugfix=20for=20previous=20commit:=20Don?= =?UTF-8?q?=E2=80=99t=20crash=20with=20dock=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/con.c | 6 +++++- src/x.c | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/con.c b/src/con.c index 6f2a85b2..eb733355 100644 --- a/src/con.c +++ b/src/con.c @@ -936,8 +936,12 @@ Con *con_descend_direction(Con *con, direction_t direction) { Rect con_border_style_rect(Con *con) { adjacent_t borders_to_hide = ADJ_NONE; Rect result; + /* Shortcut to avoid calling con_adjacent_borders() on dock containers. */ + int border_style = con_border_style(con); + if (border_style == BS_NONE) + return (Rect){ 0, 0, 0, 0 }; borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; - switch (con_border_style(con)) { + switch (border_style) { case BS_NORMAL: result = (Rect){2, 0, -(2 * 2), -2}; if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { diff --git a/src/x.c b/src/x.c index cd85bcc6..9aea9fb8 100644 --- a/src/x.c +++ b/src/x.c @@ -305,13 +305,14 @@ void x_draw_decoration(Con *con) { * • non-leaf containers which are in a stacked/tabbed container * * It does not need to run for: - * • direct children of outputs + * • direct children of outputs or dockareas * • floating containers (they don’t have a decoration) */ if ((!leaf && parent->layout != L_STACKED && parent->layout != L_TABBED) || parent->type == CT_OUTPUT || + parent->type == CT_DOCKAREA || con->type == CT_FLOATING_CON) return; From 9d9a1d9d2901cbb683d07c16f1276ce6e5e2cc8c Mon Sep 17 00:00:00 2001 From: Philipp Middendorf Date: Mon, 6 Aug 2012 18:39:38 +0200 Subject: [PATCH 082/200] Bugfix: Add deco_height only when in "normal" border mode. Also, use con_border_style_rect instead of hard-coded values to determine the border size. --- src/floating.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/floating.c b/src/floating.c index 16aab992..1f0925f3 100644 --- a/src/floating.c +++ b/src/floating.c @@ -175,11 +175,15 @@ void floating_enable(Con *con, bool automatic) { nc->rect.width = max(nc->rect.width, config.floating_minimum_width); } - /* add pixels for the decoration */ - /* TODO: don’t add them when the user automatically puts new windows into - * 1pixel/borderless mode */ - nc->rect.height += deco_height + 2; - nc->rect.width += 4; + /* Add pixels for the decoration. */ + Rect border_style_rect = con_border_style_rect(con); + + nc->rect.height -= border_style_rect.height; + nc->rect.width -= border_style_rect.width; + + /* Add some more pixels for the title bar */ + if(con_border_style(con) == BS_NORMAL) + nc->rect.height += deco_height; /* Honor the X11 border */ nc->rect.height += con->border_width * 2; From 1a62c398ac210b4b6110320d57b2df309293b868 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 01:13:37 +0200 Subject: [PATCH 083/200] Bugfix: Attach con to nc before calling con_border_style_rect() --- src/floating.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/floating.c b/src/floating.c index 1f0925f3..85ca7a5a 100644 --- a/src/floating.c +++ b/src/floating.c @@ -175,6 +175,12 @@ void floating_enable(Con *con, bool automatic) { nc->rect.width = max(nc->rect.width, config.floating_minimum_width); } + /* 3: attach the child to the new parent container. We need to do this + * because con_border_style_rect() needs to access con->parent. */ + con->parent = nc; + con->percent = 1.0; + con->floating = FLOATING_USER_ON; + /* Add pixels for the decoration. */ Rect border_style_rect = con_border_style_rect(con); @@ -223,10 +229,6 @@ void floating_enable(Con *con, bool automatic) { DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height); - /* 3: attach the child to the new parent container */ - con->parent = nc; - con->percent = 1.0; - con->floating = FLOATING_USER_ON; /* 4: set the border style as specified with new_float */ if (automatic) From 122130d868815fc185efe87c3d59a0b8a88b1d9e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 09:50:25 +0200 Subject: [PATCH 084/200] =?UTF-8?q?handlers.c:=20don=E2=80=99t=20call=20x?= =?UTF-8?q?=5Fpush=5Fchanges(croot),=20tree=5Frender()=20already=20does?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/handlers.c b/src/handlers.c index b8a7ccdf..7d8e1e25 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -622,7 +622,6 @@ static void handle_client_message(xcb_client_message_event_t *event) { } tree_render(); - x_push_changes(croot); } else if (event->type == A_I3_SYNC) { xcb_window_t window = event->data.data32[0]; uint32_t rnd = event->data.data32[1]; From fa4894fbaafb48c2b0ae95e206fafde6beab4c67 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 09:50:47 +0200 Subject: [PATCH 085/200] Support _NET_ACTIVE_WINDOW ClientMessages Since we advertise _NET_ACTIVE_WINDOW support (but only set the corresponding atom currently), it makes sense to also support the ClientMessage. Apps such as Gajim use it to set focus to the roster window when clicking on the tray icon for example. fixes #767 --- src/handlers.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/handlers.c b/src/handlers.c index 7d8e1e25..2e1e04db 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -621,6 +621,16 @@ static void handle_client_message(xcb_client_message_event_t *event) { con_toggle_fullscreen(con, CF_OUTPUT); } + tree_render(); + } else if (event->type == A__NET_ACTIVE_WINDOW) { + DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window); + Con *con = con_by_window_id(event->window); + if (con == NULL) { + DLOG("Could not get window for client message\n"); + return; + } + + con_focus(con); tree_render(); } else if (event->type == A_I3_SYNC) { xcb_window_t window = event->data.data32[0]; From 0e752070ac2bed02d0858bbc450ddcee36e3b9b5 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 09:55:52 +0200 Subject: [PATCH 086/200] explicitly set filenames to $(basename __FILE__) This makes the debug log a bit more readable, especially since commit 48f1e383ca2322ffbfbc66cd7c0325b97f8d36a9 --- src/assignments.c | 1 + src/click.c | 1 + src/commands.c | 1 + src/commands_parser.c | 1 + src/con.c | 1 + src/config.c | 1 + src/debug.c | 1 + src/ewmh.c | 1 + src/fake_outputs.c | 1 + src/floating.c | 1 + src/handlers.c | 1 + src/ipc.c | 1 + src/key_press.c | 1 + src/load_layout.c | 1 + src/log.c | 1 + src/main.c | 1 + src/manage.c | 1 + src/match.c | 1 + src/move.c | 1 + src/output.c | 1 + src/randr.c | 1 + src/regex.c | 1 + src/render.c | 1 + src/resize.c | 1 + src/scratchpad.c | 1 + src/sighandler.c | 1 + src/startup.c | 1 + src/tree.c | 1 + src/util.c | 1 + src/window.c | 1 + src/workspace.c | 1 + src/x.c | 1 + src/xcb.c | 1 + src/xcursor.c | 1 + src/xinerama.c | 1 + 35 files changed, 35 insertions(+) diff --git a/src/assignments.c b/src/assignments.c index ae4affaa..696eca49 100644 --- a/src/assignments.c +++ b/src/assignments.c @@ -1,3 +1,4 @@ +#line 2 "assignments.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/click.c b/src/click.c index 9f0549fa..52a7d3b5 100644 --- a/src/click.c +++ b/src/click.c @@ -1,3 +1,4 @@ +#line 2 "click.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/commands.c b/src/commands.c index b730bc44..6d508af9 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,3 +1,4 @@ +#line 2 "commands.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/commands_parser.c b/src/commands_parser.c index 6cfac202..71f1c149 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -1,3 +1,4 @@ +#line 2 "commands_parser.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/con.c b/src/con.c index eb733355..8f51df31 100644 --- a/src/con.c +++ b/src/con.c @@ -1,3 +1,4 @@ +#line 2 "con.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/config.c b/src/config.c index 50ec2823..4757db20 100644 --- a/src/config.c +++ b/src/config.c @@ -1,3 +1,4 @@ +#line 2 "config.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/debug.c b/src/debug.c index 30822353..4841840f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,3 +1,4 @@ +#line 2 "debug.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/ewmh.c b/src/ewmh.c index 1c6d918b..afc4eb90 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -1,3 +1,4 @@ +#line 2 "ewmh.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 512a808f..7c957225 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -1,3 +1,4 @@ +#line 2 "fake_outputs.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/floating.c b/src/floating.c index 85ca7a5a..c47884bc 100644 --- a/src/floating.c +++ b/src/floating.c @@ -1,3 +1,4 @@ +#line 2 "floating.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/handlers.c b/src/handlers.c index 2e1e04db..2a767eb2 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,3 +1,4 @@ +#line 2 "handlers.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/ipc.c b/src/ipc.c index 642b6b97..967e8871 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,3 +1,4 @@ +#line 2 "ipc.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/key_press.c b/src/key_press.c index 68f3865a..e8fbe17c 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -1,3 +1,4 @@ +#line 2 "key_press.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/load_layout.c b/src/load_layout.c index 8596dfc4..2f2d5a64 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -1,3 +1,4 @@ +#line 2 "load_layout.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/log.c b/src/log.c index d6f277f2..f7932926 100644 --- a/src/log.c +++ b/src/log.c @@ -1,3 +1,4 @@ +#line 2 "log.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/main.c b/src/main.c index 2c3dc7e9..6bd08b1e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#line 2 "main.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/manage.c b/src/manage.c index ea060d97..55157eba 100644 --- a/src/manage.c +++ b/src/manage.c @@ -1,3 +1,4 @@ +#line 2 "manage.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/match.c b/src/match.c index e92a95d2..08ec0310 100644 --- a/src/match.c +++ b/src/match.c @@ -1,3 +1,4 @@ +#line 2 "match.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/move.c b/src/move.c index d110312a..a4fc77b3 100644 --- a/src/move.c +++ b/src/move.c @@ -1,3 +1,4 @@ +#line 2 "move.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/output.c b/src/output.c index a54cb6f3..dc676ba4 100644 --- a/src/output.c +++ b/src/output.c @@ -1,3 +1,4 @@ +#line 2 "output.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/randr.c b/src/randr.c index b691a332..e1175473 100644 --- a/src/randr.c +++ b/src/randr.c @@ -1,3 +1,4 @@ +#line 2 "randr.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/regex.c b/src/regex.c index e5698d02..93e1af2d 100644 --- a/src/regex.c +++ b/src/regex.c @@ -1,3 +1,4 @@ +#line 2 "regex.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/render.c b/src/render.c index bad67f04..f933433f 100644 --- a/src/render.c +++ b/src/render.c @@ -1,3 +1,4 @@ +#line 2 "render.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/resize.c b/src/resize.c index 4b3289cd..b0c7a052 100644 --- a/src/resize.c +++ b/src/resize.c @@ -1,3 +1,4 @@ +#line 2 "resize.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/scratchpad.c b/src/scratchpad.c index 5f65bba8..acc3c557 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -1,3 +1,4 @@ +#line 2 "scratchpad.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/sighandler.c b/src/sighandler.c index fa608ed8..a218b021 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -1,3 +1,4 @@ +#line 2 "sighandler.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/startup.c b/src/startup.c index bcc2415a..b462a853 100644 --- a/src/startup.c +++ b/src/startup.c @@ -1,3 +1,4 @@ +#line 2 "startup.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/tree.c b/src/tree.c index 650c9a54..d6e7ecd9 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,3 +1,4 @@ +#line 2 "tree.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/util.c b/src/util.c index 76a74db1..45e63e1c 100644 --- a/src/util.c +++ b/src/util.c @@ -1,3 +1,4 @@ +#line 2 "util.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/window.c b/src/window.c index e9e61f16..15f722e2 100644 --- a/src/window.c +++ b/src/window.c @@ -1,3 +1,4 @@ +#line 2 "window.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/workspace.c b/src/workspace.c index 6394084a..8e834dbd 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -1,3 +1,4 @@ +#line 2 "workspace.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/x.c b/src/x.c index 9aea9fb8..661dece6 100644 --- a/src/x.c +++ b/src/x.c @@ -1,3 +1,4 @@ +#line 2 "x.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xcb.c b/src/xcb.c index 4d7a8c47..3d2ff670 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -1,3 +1,4 @@ +#line 2 "xcb.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xcursor.c b/src/xcursor.c index 058b7ae0..48f713a7 100644 --- a/src/xcursor.c +++ b/src/xcursor.c @@ -1,3 +1,4 @@ +#line 2 "xcursor.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xinerama.c b/src/xinerama.c index f377840f..5c0504ca 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -1,3 +1,4 @@ +#line 2 "xinerama.c" /* * vim:ts=4:sw=4:expandtab * From 95cdfe5cb6e91d8ae7c4413aaa64b9c4174809cb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 10:03:10 +0200 Subject: [PATCH 087/200] makefile: remove the old loglevels.tmp and loglevels.h on 'make clean' --- src/i3.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i3.mk b/src/i3.mk index f997c821..f56a60a4 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -82,4 +82,4 @@ install-i3: i3 clean-i3: echo "[i3] Clean" - rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} + rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} From 3cdc5c5369efb10cdd46498cf478e824fb1a870f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 7 Aug 2012 10:03:37 +0200 Subject: [PATCH 088/200] update .gitignore (Thanks SardemFF7) --- .gitignore | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 3eac9270..e50eb4fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,25 @@ *.o tags include/GENERATED_*.h +include/all.h.pch *.swp *.gcda *.gcno testcases/testsuite-* testcases/latest testcases/Makefile +testcases/Makefile.old +testcases/.last_run_timings.json +testcases/_Inline +testcases/inc +testcases/META.yml test.commands_parser *.output *.tab.* *.yy.c -man/i3-msg.1 -man/i3-msg.xml -man/i3-msg.html -man/i3-nagbar.1 -man/i3-nagbar.xml -man/i3-nagbar.html -man/i3-wsbar.1 -man/i3-wsbar.xml -man/i3-wsbar.html -man/i3-input.1 -man/i3-input.xml -man/i3-input.html -man/i3.1 -man/i3.xml -man/i3.html +man/*.1 +man/*.xml +man/*.html *.tar.bz2* i3 i3-input/i3-input @@ -35,3 +29,5 @@ i3-config-wizard/i3-config-wizard i3-dump-log/i3-dump-log libi3.a docs/*.pdf +docs/*.html +!/docs/refcard.html From 6ba09444308ac4c8c55beaef416cbaafe7470fb0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 8 Aug 2012 16:22:03 +0200 Subject: [PATCH 089/200] scratchpad: fix moving scratchpad window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the source: When starting i3 initially (and after each change to the connected outputs), this function fixes the resolution of the __i3 pseudo-output. When that resolution is not set to a function which shares a common divisor with every active output’s resolution, floating point calculation errors will lead to the scratchpad window moving when shown repeatedly. fixes #632 --- include/scratchpad.h | 10 ++++ src/handlers.c | 3 + src/main.c | 2 + src/scratchpad.c | 64 +++++++++++++++++++++ src/tree.c | 4 +- testcases/t/505-scratchpad-resolution.t | 76 +++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 testcases/t/505-scratchpad-resolution.t diff --git a/include/scratchpad.h b/include/scratchpad.h index 4fb7523a..4d553327 100644 --- a/include/scratchpad.h +++ b/include/scratchpad.h @@ -30,4 +30,14 @@ void scratchpad_move(Con *con); */ void scratchpad_show(Con *con); +/** + * When starting i3 initially (and after each change to the connected outputs), + * this function fixes the resolution of the __i3 pseudo-output. When that + * resolution is not set to a function which shares a common divisor with every + * active output’s resolution, floating point calculation errors will lead to + * the scratchpad window moving when shown repeatedly. + * + */ +void scratchpad_fix_resolution(void); + #endif diff --git a/src/handlers.c b/src/handlers.c index 2a767eb2..3a8d2344 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -212,6 +212,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) { Con *con; if ((con = con_by_frame_id(event->event)) == NULL) { + DLOG("MotionNotify for an unknown container, checking if it crosses screen boundaries.\n"); check_crossing_screen_boundary(event->root_x, event->root_y); return; } @@ -405,6 +406,8 @@ static void handle_screen_change(xcb_generic_event_t *e) { randr_query_outputs(); + scratchpad_fix_resolution(); + ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}"); return; diff --git a/src/main.c b/src/main.c index 6bd08b1e..e75e7fbf 100644 --- a/src/main.c +++ b/src/main.c @@ -657,6 +657,8 @@ int main(int argc, char *argv[]) { randr_init(&randr_base); } + scratchpad_fix_resolution(); + xcb_query_pointer_reply_t *pointerreply; Output *output = NULL; if (!(pointerreply = xcb_query_pointer_reply(conn, pointercookie, NULL))) { diff --git a/src/scratchpad.c b/src/scratchpad.c index acc3c557..da50cee7 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -141,3 +141,67 @@ void scratchpad_show(Con *con) { con_focus(con_descend_focused(con)); } + +/* + * Greatest common divisor, implemented only for the least common multiple + * below. + * + */ +static int _gcd(const int m, const int n) { + if (n == 0) + return m; + return _gcd(n, (m % n)); +} + +/* + * Least common multiple. We use it to determine the (ideally not too large) + * resolution for the __i3 pseudo-output on which the scratchpad is on (see + * below). We could just multiply the resolutions, but for some pathetic cases + * (many outputs), using the LCM will achieve better results. + * + * Man, when you were learning about these two algorithms for the first time, + * did you think you’d ever need them in a real-world software project of + * yours? I certainly didn’t until now. :-D + * + */ +static int _lcm(const int m, const int n) { + const int o = _gcd(m, n); + return ((m * n) / o); +} + +/* + * When starting i3 initially (and after each change to the connected outputs), + * this function fixes the resolution of the __i3 pseudo-output. When that + * resolution is not set to a function which shares a common divisor with every + * active output’s resolution, floating point calculation errors will lead to + * the scratchpad window moving when shown repeatedly. + * + */ +void scratchpad_fix_resolution(void) { + Con *__i3_scratch = workspace_get("__i3_scratch", NULL); + Con *__i3_output = con_get_output(__i3_scratch); + DLOG("Current resolution: (%d, %d) %d x %d\n", + __i3_output->rect.x, __i3_output->rect.y, + __i3_output->rect.width, __i3_output->rect.height); + Con *output; + int new_width = -1, + new_height = -1; + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + if (output == __i3_output) + continue; + DLOG("output %s's resolution: (%d, %d) %d x %d\n", + output->name, output->rect.x, output->rect.y, + output->rect.width, output->rect.height); + if (new_width == -1) { + new_width = output->rect.width; + new_height = output->rect.height; + } else { + new_width = _lcm(new_width, output->rect.width); + new_height = _lcm(new_height, output->rect.height); + } + } + DLOG("new width = %d, new height = %d\n", + new_width, new_height); + __i3_output->rect.width = new_width; + __i3_output->rect.height = new_height; +} diff --git a/src/tree.c b/src/tree.c index d6e7ecd9..cee61b9d 100644 --- a/src/tree.c +++ b/src/tree.c @@ -30,7 +30,9 @@ static Con *_create___i3(void) { x_set_name(__i3, "[i3 con] pseudo-output __i3"); /* For retaining the correct position/size of a scratchpad window, the * dimensions of the real outputs should be multiples of the __i3 - * pseudo-output. */ + * pseudo-output. Ensuring that is the job of scratchpad_fix_resolution() + * which gets called after this function and after detecting all the + * outputs (or whenever an output changes). */ __i3->rect.width = 1280; __i3->rect.height = 1024; diff --git a/testcases/t/505-scratchpad-resolution.t b/testcases/t/505-scratchpad-resolution.t new file mode 100644 index 00000000..3d0bcfa6 --- /dev/null +++ b/testcases/t/505-scratchpad-resolution.t @@ -0,0 +1,76 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Verifies that scratchpad windows don’t move due to floating point caulcation +# errors when repeatedly hiding/showing, no matter what display resolution. +# +use i3test i3_autostart => 0; + +my $config = <root->warp_pointer(0, 0); +sync_with_i3; + +sub verify_scratchpad_doesnt_move { + my ($ws) = @_; + + is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + + my $window = open_window; + + is(scalar @{get_ws($ws)->{nodes}}, 1, 'one nodes on this ws'); + + cmd 'move scratchpad'; + + is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + + my $last_x = -1; + for (1 .. 20) { + cmd 'scratchpad show'; + is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on this ws'); + + # Verify that the coordinates are within bounds. + my $content = get_ws($ws); + my $srect = $content->{floating_nodes}->[0]->{rect}; + if ($last_x > -1) { + is($srect->{x}, $last_x, 'scratchpad window did not move'); + } + $last_x = $srect->{x}; + cmd 'scratchpad show'; + } + + # We need to kill the scratchpad window, otherwise scratchpad show in + # subsequent calls of verify_scratchpad_doesnt_move will cycle between all + # the windows. + cmd 'scratchpad show'; + cmd 'kill'; +} + +################################################################################ +# test it on the left output first (1366x768) +################################################################################ + +my $second = fresh_workspace(output => 0); +verify_scratchpad_doesnt_move($second); + +################################################################################ +# now on the right output (1024x768) +################################################################################ + +$x->root->warp_pointer(683 + 10, 0); +sync_with_i3; + +my $third = fresh_workspace(output => 1); +verify_scratchpad_doesnt_move($third); + +exit_gracefully($pid); + +done_testing; From da924aae6fd33a3e823b67fd1b1ea011099328f9 Mon Sep 17 00:00:00 2001 From: Valentin Haenel Date: Fri, 10 Aug 2012 23:00:07 +0200 Subject: [PATCH 090/200] Bugfix: bump copyright in asciidoc-git.conf --- docs/asciidoc-git.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/asciidoc-git.conf b/docs/asciidoc-git.conf index 24dcb596..cc135ae9 100644 --- a/docs/asciidoc-git.conf +++ b/docs/asciidoc-git.conf @@ -647,7 +647,7 @@ endif::doctype-manpage[]

{disable-javascript%

} From 70ec3867fe0fdf5d97b5680fdb5f670474bceaa7 Mon Sep 17 00:00:00 2001 From: Julius Plenz Date: Sat, 11 Aug 2012 01:50:37 +0200 Subject: [PATCH 091/200] clean up zero-byte logfile on immediate exit Otherwise, a zero-byte log file stays behind after every call to `i3 --get-socketpath`. Also, replace "return" calls with more explicit "exit" calls. Before: $ ls -ld /tmp/i3* | wc -l; \ repeat 10 i3 --get-socketpath >/dev/null; \ ls -ld /tmp/i3* | wc -l 1 11 Now: $ ls -ld /tmp/i3* | wc -l; \ repeat 10 i3 --get-socketpath >/dev/null; \ ls -ld /tmp/i3* | wc -l 1 1 Signed-off-by: Julius Plenz --- include/log.h | 7 +++++++ src/log.c | 28 ++++++++++++++++++++++++++++ src/main.c | 4 ++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/include/log.h b/include/log.h index 0ad0fb37..a8209cca 100644 --- a/include/log.h +++ b/include/log.h @@ -67,4 +67,11 @@ void errorlog(char *fmt, ...) void verboselog(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +/** + * Deletes the unused log files. Useful if i3 exits immediately, eg. + * because --get-socketpath was called. We don't care for syscall + * failures. This function is invoked automatically when exiting. + */ +void purge_zerobyte_logfile(void); + #endif diff --git a/src/log.c b/src/log.c index f7932926..944edb3f 100644 --- a/src/log.c +++ b/src/log.c @@ -131,6 +131,7 @@ void init_logging(void) { loglastwrap = logbuffer + logbuffer_size; store_log_markers(); } + atexit(purge_zerobyte_logfile); } /* @@ -268,3 +269,30 @@ void debuglog(char *fmt, ...) { vlog(debug_logging, fmt, args); va_end(args); } + +/* + * Deletes the unused log files. Useful if i3 exits immediately, eg. + * because --get-socketpath was called. We don't care for syscall + * failures. This function is invoked automatically when exiting. + */ +void purge_zerobyte_logfile(void) { + struct stat st; + char *slash; + + if (!errorfilename) + return; + + /* don't delete the log file if it contains something */ + if ((stat(errorfilename, &st)) == -1 || st.st_size > 0) + return; + + if (unlink(errorfilename) == -1) + return; + + if ((slash = strrchr(errorfilename, '/')) != NULL) { + *slash = '\0'; + /* possibly fails with ENOTEMPTY if there are files (or + * sockets) left. */ + rmdir(errorfilename); + } +} diff --git a/src/main.c b/src/main.c index e75e7fbf..d6a7f617 100644 --- a/src/main.c +++ b/src/main.c @@ -343,10 +343,10 @@ int main(int argc, char *argv[]) { char *socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path) { printf("%s\n", socket_path); - return 0; + exit(EXIT_SUCCESS); } - return 1; + exit(EXIT_FAILURE); } else if (strcmp(long_options[option_index].name, "shmlog-size") == 0 || strcmp(long_options[option_index].name, "shmlog_size") == 0) { shmlog_size = atoi(optarg); From 91786c380115d8d5dfff11aed31cabe560e317c9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 11 Aug 2012 22:53:40 +0200 Subject: [PATCH 092/200] typo: s/transiert/transient/ --- src/manage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manage.c b/src/manage.c index 55157eba..2d08afaa 100644 --- a/src/manage.c +++ b/src/manage.c @@ -324,7 +324,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki (cwindow->leader != XCB_NONE && cwindow->leader != cwindow->id && con_by_window_id(cwindow->leader) != NULL)) { - LOG("This window is transiert for another window, setting floating\n"); + LOG("This window is transient for another window, setting floating\n"); want_floating = true; if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && From d92626bc62b8f1da53bee99710228c0b755ac3f7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 00:20:52 +0200 Subject: [PATCH 093/200] complete-run: enable autoflush (useful for debugging) I used the following command to find hanging workers: sort latest/complete-run.log|cut -f 2 -d ' '|uniq -c|grep -v '^[ \t]*2' --- testcases/complete-run.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index f5520603..88fece4b 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -15,6 +15,7 @@ use TAP::Harness; use TAP::Parser; use TAP::Parser::Aggregator; use Time::HiRes qw(time); +use IO::Handle; # these are shipped with the testsuite use lib qw(lib); use StartXDummy; @@ -127,6 +128,7 @@ printf("\nRough time estimate for this run: %.2f seconds\n\n", $timings{GLOBAL}) my $logfile = "$outdir/complete-run.log"; open $log, '>', $logfile or die "Could not create '$logfile': $!"; +$log->autoflush(1); say "Writing logfile to '$logfile'..."; # 3: run all tests From ae88accf6fe3817ff42d0d51be1965071194766e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 00:22:17 +0200 Subject: [PATCH 094/200] testworker: handle EAGAIN Hopefully this fixes the case where a testworker would die and leave the whole testsuite run hanging in the air, never completing. --- testcases/lib/TestWorker.pm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testcases/lib/TestWorker.pm b/testcases/lib/TestWorker.pm index a224b718..140537d4 100644 --- a/testcases/lib/TestWorker.pm +++ b/testcases/lib/TestWorker.pm @@ -8,6 +8,8 @@ use IO::Handle; # for ->autoflush use POSIX (); +use Errno qw(EAGAIN); + use Exporter 'import'; our @EXPORT = qw(worker worker_next); @@ -74,7 +76,12 @@ sub worker_wait { my $ipc = $self->{ipc}; my $ipc_fd = fileno($ipc); - while (defined(my $file = $ipc->getline)) { + while (1) { + my $file = $ipc->getline; + if (!defined($file)) { + next if $! == EAGAIN; + last; + } chomp $file; exit unless $file; From 6225a8983d159e9afb8044c7be1232367bfeeaae Mon Sep 17 00:00:00 2001 From: Philipp Middendorf Date: Thu, 9 Aug 2012 12:32:35 +0200 Subject: [PATCH 095/200] Bugfix: Properly resize transient floating windows with a decoration fixes #770 --- src/floating.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/floating.c b/src/floating.c index c47884bc..69241b58 100644 --- a/src/floating.c +++ b/src/floating.c @@ -182,6 +182,10 @@ void floating_enable(Con *con, bool automatic) { con->percent = 1.0; con->floating = FLOATING_USER_ON; + /* 4: set the border style as specified with new_float */ + if (automatic) + con->border_style = config.default_floating_border; + /* Add pixels for the decoration. */ Rect border_style_rect = con_border_style_rect(con); @@ -230,11 +234,6 @@ void floating_enable(Con *con, bool automatic) { DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height); - - /* 4: set the border style as specified with new_float */ - if (automatic) - con->border_style = config.default_floating_border; - /* 5: Subtract the deco_height in order to make the floating window appear * at precisely the position it specified in its original geometry (which * is what applications might remember). */ From dd743f3b55b2f86d9f1f69ef7952ae8ece4de504 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 00:36:44 +0200 Subject: [PATCH 096/200] add testcase for previous bugfix (floating window size) --- testcases/t/194-regress-floating-size.t | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 testcases/t/194-regress-floating-size.t diff --git a/testcases/t/194-regress-floating-size.t b/testcases/t/194-regress-floating-size.t new file mode 100644 index 00000000..ccbfaae5 --- /dev/null +++ b/testcases/t/194-regress-floating-size.t @@ -0,0 +1,43 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verifies that the size requested by floating windows is set by i3, no matter +# to which value the new_window option is set. +# ticket #770, bug still present in commit ae88accf6fe3817ff42d0d51be1965071194766e +use i3test i3_autostart => 0; + +sub test_with_new_window_value { + my ($value) = @_; + + my $config = < [ 0, 0, 400, 150 ] }); + + my ($absolute, $top) = $window->rect; + + ok($window->mapped, 'Window is mapped'); + cmp_ok($absolute->{width}, '==', 400, 'requested width kept'); + cmp_ok($absolute->{height}, '==', 150, 'requested height kept'); + + exit_gracefully($pid); +} + +test_with_new_window_value(undef); +test_with_new_window_value('1pixel'); +test_with_new_window_value('normal'); +test_with_new_window_value('none'); + +done_testing; From 884627ef20dfb2edae9a5c99bb470bfbf0301904 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 12:18:43 +0200 Subject: [PATCH 097/200] use I3__FILE__ for DLOG, leave __FILE__ as is See also commit 0e752070ac2bed02d0858bbc450ddcee36e3b9b5, which broke source code listings in gdb unless you cd into i3/src. This should give us best of both :-). --- common.mk | 1 + include/log.h | 2 +- src/assignments.c | 3 ++- src/click.c | 3 ++- src/commands.c | 3 ++- src/commands_parser.c | 3 ++- src/con.c | 3 ++- src/config.c | 3 ++- src/debug.c | 3 ++- src/ewmh.c | 3 ++- src/fake_outputs.c | 3 ++- src/floating.c | 3 ++- src/handlers.c | 3 ++- src/ipc.c | 3 ++- src/key_press.c | 3 ++- src/load_layout.c | 3 ++- src/log.c | 3 ++- src/main.c | 3 ++- src/manage.c | 3 ++- src/match.c | 3 ++- src/move.c | 3 ++- src/output.c | 3 ++- src/randr.c | 3 ++- src/regex.c | 3 ++- src/render.c | 3 ++- src/resize.c | 3 ++- src/scratchpad.c | 3 ++- src/sighandler.c | 3 ++- src/startup.c | 3 ++- src/tree.c | 3 ++- src/util.c | 3 ++- src/window.c | 3 ++- src/workspace.c | 3 ++- src/x.c | 3 ++- src/xcb.c | 3 ++- src/xcursor.c | 3 ++- src/xinerama.c | 3 ++- 37 files changed, 72 insertions(+), 36 deletions(-) diff --git a/common.mk b/common.mk index 0d2ad51e..a6d6c4e3 100644 --- a/common.mk +++ b/common.mk @@ -56,6 +56,7 @@ I3_CPPFLAGS += -DMAJOR_VERSION=${MAJOR_VERSION} I3_CPPFLAGS += -DMINOR_VERSION=${MINOR_VERSION} I3_CPPFLAGS += -DPATCH_VERSION=${PATCH_VERSION} I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" +I3_CPPFLAGS += -DI3__FILE__=__FILE__ ## Libraries flags diff --git a/include/log.h b/include/log.h index a8209cca..0854628d 100644 --- a/include/log.h +++ b/include/log.h @@ -17,7 +17,7 @@ is, delete the preceding comma */ #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__) -#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) extern char *errorfilename; extern char *shmlogname; diff --git a/src/assignments.c b/src/assignments.c index 696eca49..655816a3 100644 --- a/src/assignments.c +++ b/src/assignments.c @@ -1,4 +1,5 @@ -#line 2 "assignments.c" +#undef I3__FILE__ +#define I3__FILE__ "assignments.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/click.c b/src/click.c index 52a7d3b5..23b6be4f 100644 --- a/src/click.c +++ b/src/click.c @@ -1,4 +1,5 @@ -#line 2 "click.c" +#undef I3__FILE__ +#define I3__FILE__ "click.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/commands.c b/src/commands.c index 6d508af9..02d602f0 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,4 +1,5 @@ -#line 2 "commands.c" +#undef I3__FILE__ +#define I3__FILE__ "commands.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/commands_parser.c b/src/commands_parser.c index 71f1c149..20a7d67a 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -1,4 +1,5 @@ -#line 2 "commands_parser.c" +#undef I3__FILE__ +#define I3__FILE__ "commands_parser.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/con.c b/src/con.c index 8f51df31..0c82163a 100644 --- a/src/con.c +++ b/src/con.c @@ -1,4 +1,5 @@ -#line 2 "con.c" +#undef I3__FILE__ +#define I3__FILE__ "con.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/config.c b/src/config.c index 4757db20..c256a3b7 100644 --- a/src/config.c +++ b/src/config.c @@ -1,4 +1,5 @@ -#line 2 "config.c" +#undef I3__FILE__ +#define I3__FILE__ "config.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/debug.c b/src/debug.c index 4841840f..2dcdb56a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,4 +1,5 @@ -#line 2 "debug.c" +#undef I3__FILE__ +#define I3__FILE__ "debug.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/ewmh.c b/src/ewmh.c index afc4eb90..45d4e5fe 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -1,4 +1,5 @@ -#line 2 "ewmh.c" +#undef I3__FILE__ +#define I3__FILE__ "ewmh.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 7c957225..e1153299 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -1,4 +1,5 @@ -#line 2 "fake_outputs.c" +#undef I3__FILE__ +#define I3__FILE__ "fake_outputs.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/floating.c b/src/floating.c index 69241b58..3d2c1d31 100644 --- a/src/floating.c +++ b/src/floating.c @@ -1,4 +1,5 @@ -#line 2 "floating.c" +#undef I3__FILE__ +#define I3__FILE__ "floating.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/handlers.c b/src/handlers.c index 3a8d2344..a69a6269 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,4 +1,5 @@ -#line 2 "handlers.c" +#undef I3__FILE__ +#define I3__FILE__ "handlers.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/ipc.c b/src/ipc.c index 967e8871..8db69259 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,4 +1,5 @@ -#line 2 "ipc.c" +#undef I3__FILE__ +#define I3__FILE__ "ipc.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/key_press.c b/src/key_press.c index e8fbe17c..2a578d66 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -1,4 +1,5 @@ -#line 2 "key_press.c" +#undef I3__FILE__ +#define I3__FILE__ "key_press.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/load_layout.c b/src/load_layout.c index 2f2d5a64..795fb6d8 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -1,4 +1,5 @@ -#line 2 "load_layout.c" +#undef I3__FILE__ +#define I3__FILE__ "load_layout.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/log.c b/src/log.c index 944edb3f..ab75395f 100644 --- a/src/log.c +++ b/src/log.c @@ -1,4 +1,5 @@ -#line 2 "log.c" +#undef I3__FILE__ +#define I3__FILE__ "log.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/main.c b/src/main.c index d6a7f617..b60bb6d7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ -#line 2 "main.c" +#undef I3__FILE__ +#define I3__FILE__ "main.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/manage.c b/src/manage.c index 2d08afaa..1dc39b9e 100644 --- a/src/manage.c +++ b/src/manage.c @@ -1,4 +1,5 @@ -#line 2 "manage.c" +#undef I3__FILE__ +#define I3__FILE__ "manage.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/match.c b/src/match.c index 08ec0310..1014de84 100644 --- a/src/match.c +++ b/src/match.c @@ -1,4 +1,5 @@ -#line 2 "match.c" +#undef I3__FILE__ +#define I3__FILE__ "match.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/move.c b/src/move.c index a4fc77b3..46b90177 100644 --- a/src/move.c +++ b/src/move.c @@ -1,4 +1,5 @@ -#line 2 "move.c" +#undef I3__FILE__ +#define I3__FILE__ "move.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/output.c b/src/output.c index dc676ba4..fe8d4983 100644 --- a/src/output.c +++ b/src/output.c @@ -1,4 +1,5 @@ -#line 2 "output.c" +#undef I3__FILE__ +#define I3__FILE__ "output.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/randr.c b/src/randr.c index e1175473..6971ba97 100644 --- a/src/randr.c +++ b/src/randr.c @@ -1,4 +1,5 @@ -#line 2 "randr.c" +#undef I3__FILE__ +#define I3__FILE__ "randr.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/regex.c b/src/regex.c index 93e1af2d..60dee5cc 100644 --- a/src/regex.c +++ b/src/regex.c @@ -1,4 +1,5 @@ -#line 2 "regex.c" +#undef I3__FILE__ +#define I3__FILE__ "regex.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/render.c b/src/render.c index f933433f..9c0debb3 100644 --- a/src/render.c +++ b/src/render.c @@ -1,4 +1,5 @@ -#line 2 "render.c" +#undef I3__FILE__ +#define I3__FILE__ "render.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/resize.c b/src/resize.c index b0c7a052..b65344a2 100644 --- a/src/resize.c +++ b/src/resize.c @@ -1,4 +1,5 @@ -#line 2 "resize.c" +#undef I3__FILE__ +#define I3__FILE__ "resize.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/scratchpad.c b/src/scratchpad.c index da50cee7..6bba823f 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -1,4 +1,5 @@ -#line 2 "scratchpad.c" +#undef I3__FILE__ +#define I3__FILE__ "scratchpad.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/sighandler.c b/src/sighandler.c index a218b021..bae73c16 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -1,4 +1,5 @@ -#line 2 "sighandler.c" +#undef I3__FILE__ +#define I3__FILE__ "sighandler.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/startup.c b/src/startup.c index b462a853..b0aa2ca3 100644 --- a/src/startup.c +++ b/src/startup.c @@ -1,4 +1,5 @@ -#line 2 "startup.c" +#undef I3__FILE__ +#define I3__FILE__ "startup.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/tree.c b/src/tree.c index cee61b9d..d2dc10ea 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,4 +1,5 @@ -#line 2 "tree.c" +#undef I3__FILE__ +#define I3__FILE__ "tree.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/util.c b/src/util.c index 45e63e1c..db2f0204 100644 --- a/src/util.c +++ b/src/util.c @@ -1,4 +1,5 @@ -#line 2 "util.c" +#undef I3__FILE__ +#define I3__FILE__ "util.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/window.c b/src/window.c index 15f722e2..6ec63a8b 100644 --- a/src/window.c +++ b/src/window.c @@ -1,4 +1,5 @@ -#line 2 "window.c" +#undef I3__FILE__ +#define I3__FILE__ "window.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/workspace.c b/src/workspace.c index 8e834dbd..85331181 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -1,4 +1,5 @@ -#line 2 "workspace.c" +#undef I3__FILE__ +#define I3__FILE__ "workspace.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/x.c b/src/x.c index 661dece6..65b3eb1f 100644 --- a/src/x.c +++ b/src/x.c @@ -1,4 +1,5 @@ -#line 2 "x.c" +#undef I3__FILE__ +#define I3__FILE__ "x.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xcb.c b/src/xcb.c index 3d2ff670..be3f536a 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -1,4 +1,5 @@ -#line 2 "xcb.c" +#undef I3__FILE__ +#define I3__FILE__ "xcb.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xcursor.c b/src/xcursor.c index 48f713a7..7683b0d3 100644 --- a/src/xcursor.c +++ b/src/xcursor.c @@ -1,4 +1,5 @@ -#line 2 "xcursor.c" +#undef I3__FILE__ +#define I3__FILE__ "xcursor.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/xinerama.c b/src/xinerama.c index 5c0504ca..7e5b5aeb 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -1,4 +1,5 @@ -#line 2 "xinerama.c" +#undef I3__FILE__ +#define I3__FILE__ "xinerama.c" /* * vim:ts=4:sw=4:expandtab * From b01d45e027c386d216447d3e4bb5a58a94e96b3a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 13:46:54 +0200 Subject: [PATCH 098/200] complete-run: handle bailouts --- testcases/complete-run.pl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 88fece4b..4bdf5c78 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -265,9 +265,16 @@ sub take_job { for (1 .. $lines) { my $result = $parser->next; - if (defined($result) and $result->is_test) { + next unless defined($result); + if ($result->is_test) { $tests_completed++; status($display, "$test: [$tests_completed/??] "); + } elsif ($result->is_bailout) { + Log status($display, "$test: BAILOUT"); + status_completed(scalar @done); + say ""; + say "test $test bailed out: " . $result->explanation; + exit 1; } } From 394b395455cfcdf49a9ec314650687d4e8d428da Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 13:51:47 +0200 Subject: [PATCH 099/200] set I3_PID atom on the root window --- include/atoms.xmacro | 1 + src/x.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index b907f41e..af60b966 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -27,3 +27,4 @@ xmacro(I3_SOCKET_PATH) xmacro(I3_CONFIG_PATH) xmacro(I3_SYNC) xmacro(I3_SHMLOG_PATH) +xmacro(I3_PID) diff --git a/src/x.c b/src/x.c index 65b3eb1f..84217a85 100644 --- a/src/x.c +++ b/src/x.c @@ -1030,9 +1030,11 @@ void x_set_name(Con *con, const char *name) { * */ void x_set_i3_atoms(void) { + pid_t pid = getpid(); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SOCKET_PATH, A_UTF8_STRING, 8, (current_socketpath == NULL ? 0 : strlen(current_socketpath)), current_socketpath); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8, strlen(current_configpath), current_configpath); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, From b8e782c9836d14a14c8afcf6d42dc22055edca9a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 14:10:48 +0200 Subject: [PATCH 100/200] root_atom_contents: handle CARDINAL atoms such as I3_PID --- libi3/root_atom_contents.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libi3/root_atom_contents.c b/libi3/root_atom_contents.c index 927cc5f8..cabaaf2c 100644 --- a/libi3/root_atom_contents.c +++ b/libi3/root_atom_contents.c @@ -28,7 +28,7 @@ char *root_atom_contents(const char *atomname) { xcb_intern_atom_cookie_t atom_cookie; xcb_intern_atom_reply_t *atom_reply; int screen; - char *socket_path; + char *content; if ((conn = xcb_connect(NULL, &screen)) == NULL || xcb_connection_has_error(conn)) @@ -50,10 +50,17 @@ char *root_atom_contents(const char *atomname) { prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL); if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0) return NULL; - if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply), - (char*)xcb_get_property_value(prop_reply)) == -1) - return NULL; + if (prop_reply->type == XCB_ATOM_CARDINAL) { + /* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL + * we query is I3_PID, which is 32-bit. */ + if (asprintf(&content, "%u", *((unsigned int*)xcb_get_property_value(prop_reply))) == -1) + return NULL; + } else { + if (asprintf(&content, "%.*s", xcb_get_property_value_length(prop_reply), + (char*)xcb_get_property_value(prop_reply)) == -1) + return NULL; + } xcb_disconnect(conn); - return socket_path; + return content; } From 1e49f1b08a3035c1f238fcd6615e332216ab582e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 15:10:13 +0200 Subject: [PATCH 101/200] Implement i3 --moreversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the code: Connects to i3 to find out the currently running version. Useful since it might be different from the version compiled into this binary (maybe the user didn’t correctly install i3 or forgot te restart it). Here is an example output: $ ./i3 --moreversion Binary i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") © 2009-2012 Michael Stapelberg and contributors Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) The i3 binary you just called: /home/michael/i3/i3 RUNNING BINARY DIFFERENT FROM BINARY ON DISK! The i3 binary you are running: /home/michael/i3/i3 $ i3 restart 2012-08-12 15:05:28 - Additional arguments passed. Sending them as a command to i3. IPC: received EOF instead of reply $ ./i3 --moreversion Binary i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") © 2009-2012 Michael Stapelberg and contributors Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) The i3 binary you just called: /home/michael/i3/i3 The i3 binary you are running: /home/michael/i3/i3 --- include/all.h | 1 + include/display_version.h | 27 +++++++ src/display_version.c | 165 ++++++++++++++++++++++++++++++++++++++ src/main.c | 11 ++- 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 include/display_version.h create mode 100644 src/display_version.c diff --git a/include/all.h b/include/all.h index c6b0351f..b83b9f4e 100644 --- a/include/all.h +++ b/include/all.h @@ -80,5 +80,6 @@ #include "commands.h" #include "commands_parser.h" #include "fake_outputs.h" +#include "display_version.h" #endif diff --git a/include/display_version.h b/include/display_version.h new file mode 100644 index 00000000..97b3902c --- /dev/null +++ b/include/display_version.h @@ -0,0 +1,27 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * + * display_version.c: displays the running i3 version, runs as part of + * i3 --moreversion. + */ +#ifndef _DISPLAY_VERSION_H +#define _DISPLAY_VERSION_H + +/** + * Connects to i3 to find out the currently running version. Useful since it + * might be different from the version compiled into this binary (maybe the + * user didn’t correctly install i3 or forgot te restart it). + * + * The output looks like this: + * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) + * + * The i3 binary you just called: /home/michael/i3/i3 + * The i3 binary you are running: /home/michael/i3/i3 + * + */ +void display_running_version(void); + +#endif diff --git a/src/display_version.c b/src/display_version.c new file mode 100644 index 00000000..669e7c7d --- /dev/null +++ b/src/display_version.c @@ -0,0 +1,165 @@ +#undef I3__FILE__ +#define I3__FILE__ "key_press.c" +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * + * display_version.c: displays the running i3 version, runs as part of + * i3 --moreversion. + * + */ +#include +#include +#include +#include +#include +#include +#include "all.h" + +static bool human_readable_key; +static char *human_readable_version; + +#if YAJL_MAJOR >= 2 +static int version_string(void *ctx, const unsigned char *val, size_t len) { +#else +static int version_string(void *ctx, const unsigned char *val, unsigned int len) { +#endif + if (human_readable_key) + sasprintf(&human_readable_version, "%.*s", (int)len, val); + return 1; +} + +#if YAJL_MAJOR >= 2 +static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) { +#else +static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) { +#endif + human_readable_key = (stringlen == strlen("human_readable") && + strncmp((const char*)stringval, "human_readable", strlen("human_readable")) == 0); + return 1; +} + +static yajl_callbacks version_callbacks = { + NULL, /* null */ + NULL, /* boolean */ + NULL, /* integer */ + NULL, /* double */ + NULL, /* number */ + &version_string, + NULL, /* start_map */ + &version_map_key, + NULL, /* end_map */ + NULL, /* start_array */ + NULL /* end_array */ +}; + +/* + * Connects to i3 to find out the currently running version. Useful since it + * might be different from the version compiled into this binary (maybe the + * user didn’t correctly install i3 or forgot te restart it). + * + * The output looks like this: + * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804) + * + * The i3 binary you just called: /home/michael/i3/i3 + * The i3 binary you are running: /home/michael/i3/i3 + * + */ +void display_running_version(void) { + char *socket_path = root_atom_contents("I3_SOCKET_PATH"); + if (socket_path == NULL) + exit(EXIT_SUCCESS); + + char *pid_from_atom = root_atom_contents("I3_PID"); + if (pid_from_atom == NULL) { + /* If I3_PID is not set, the running version is older than 4.2-200. */ + printf("\nRunning version: < 4.2-200\n"); + exit(EXIT_SUCCESS); + } + + /* Inform the user of what we are doing. While a single IPC request is + * really fast normally, in case i3 hangs, this will not terminate. */ + printf("(Getting version from running i3, press ctrl-c to abort…)"); + fflush(stdout); + + /* TODO: refactor this with the code for sending commands */ + int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (sockfd == -1) + err(EXIT_FAILURE, "Could not create socket"); + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_LOCAL; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + 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, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION, + (uint8_t*)"") == -1) + err(EXIT_FAILURE, "IPC: write()"); + + uint32_t reply_length; + uint8_t *reply; + int ret; + if ((ret = ipc_recv_message(sockfd, I3_IPC_MESSAGE_TYPE_GET_VERSION, + &reply_length, &reply)) != 0) { + if (ret == -1) + err(EXIT_FAILURE, "IPC: read()"); + exit(EXIT_FAILURE); + } + +#if YAJL_MAJOR >= 2 + yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL); +#else + yajl_parser_config parse_conf = { 0, 0 }; + + yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL); +#endif + + yajl_status state = yajl_parse(handle, (const unsigned char*)reply, (int)reply_length); + if (state != yajl_status_ok) + err(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply); + + printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom); + printf("\n"); + char resolved_path[PATH_MAX]; + if (realpath(start_argv[0], resolved_path) == NULL) + err(EXIT_FAILURE, "realpath(%s)", start_argv[0]); + printf("The i3 binary you just called: %s\n", resolved_path); + +#ifdef __linux__ + char exepath[PATH_MAX], + destpath[PATH_MAX]; + snprintf(exepath, sizeof(exepath), "/proc/%s/exe", pid_from_atom); + + ssize_t linksize; + if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1) + err(EXIT_FAILURE, "readlink(%s)", exepath); + + /* readlink() does not NULL-terminate strings, so we have to. */ + destpath[linksize] = '\0'; + + /* Check if "(deleted)" is the readlink result. If so, the running version + * does not match the file on disk. */ + if (strstr(destpath, "(deleted)") != NULL) + printf("RUNNING BINARY DIFFERENT FROM BINARY ON DISK!\n"); + + /* Since readlink() might put a "(deleted)" somewhere in the buffer and + * stripping that out seems hackish and ugly, we read the process’s argv[0] + * instead. */ + snprintf(exepath, sizeof(exepath), "/proc/%s/cmdline", pid_from_atom); + + int fd; + if ((fd = open(exepath, O_RDONLY)) == -1) + err(EXIT_FAILURE, "open(%s)", exepath); + if (read(fd, destpath, sizeof(destpath)) == -1) + err(EXIT_FAILURE, "read(%s)", exepath); + close(fd); + + printf("The i3 binary you are running: %s\n", destpath); +#endif + + yajl_free(handle); +} diff --git a/src/main.c b/src/main.c index b60bb6d7..47d66b87 100644 --- a/src/main.c +++ b/src/main.c @@ -257,6 +257,9 @@ int main(int argc, char *argv[]) { {"no-autostart", no_argument, 0, 'a'}, {"config", required_argument, 0, 'c'}, {"version", no_argument, 0, 'v'}, + {"moreversion", no_argument, 0, 'm'}, + {"more-version", no_argument, 0, 'm'}, + {"more_version", no_argument, 0, 'm'}, {"help", no_argument, 0, 'h'}, {"layout", required_argument, 0, 'L'}, {"restart", required_argument, 0, 0}, @@ -294,7 +297,7 @@ int main(int argc, char *argv[]) { start_argv = argv; - while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, "c:CvmaL:hld:V", long_options, &option_index)) != -1) { switch (opt) { case 'a': LOG("Autostart disabled using -a\n"); @@ -316,6 +319,12 @@ int main(int argc, char *argv[]) { case 'v': printf("i3 version " I3_VERSION " © 2009-2012 Michael Stapelberg and contributors\n"); exit(EXIT_SUCCESS); + break; + case 'm': + printf("Binary i3 version: " I3_VERSION " © 2009-2012 Michael Stapelberg and contributors\n"); + display_running_version(); + exit(EXIT_SUCCESS); + break; case 'V': set_verbosity(true); break; From 68a23b957717c995fd6569af535f2baf91b429b3 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 16:08:31 +0200 Subject: [PATCH 102/200] Bugfix: only honor _NET_ACTIVE_WINDOW for visible windows (+test) (Thanks Tucos) fixes #774 --- src/handlers.c | 9 +++++ testcases/t/195-net-active-window.t | 54 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 testcases/t/195-net-active-window.t diff --git a/src/handlers.c b/src/handlers.c index a69a6269..31543626 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -635,6 +635,15 @@ static void handle_client_message(xcb_client_message_event_t *event) { return; } + Con *ws = con_get_workspace(con); + if (!workspace_is_visible(ws)) { + DLOG("Workspace not visible, ignoring _NET_ACTIVE_WINDOW\n"); + return; + } + + if (ws != con_get_workspace(focused)) + workspace_show(ws); + con_focus(con); tree_render(); } else if (event->type == A_I3_SYNC) { diff --git a/testcases/t/195-net-active-window.t b/testcases/t/195-net-active-window.t new file mode 100644 index 00000000..f392378a --- /dev/null +++ b/testcases/t/195-net-active-window.t @@ -0,0 +1,54 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verifies that the _NET_ACTIVE_WINDOW message only changes focus when the +# window is on a visible workspace. +# ticket #774, bug still present in commit 1e49f1b08a3035c1f238fcd6615e332216ab582e +use i3test; + +sub send_net_active_window { + my ($id) = @_; + + my $msg = pack "CCSLLLLLLL", + X11::XCB::CLIENT_MESSAGE, # response_type + 32, # format + 0, # sequence + $id, # destination window + $x->atom(name => '_NET_ACTIVE_WINDOW')->id, + 0, + 0, + 0, + 0, + 0; + + $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); +} + +my $ws1 = fresh_workspace; +my $win1 = open_window; +my $win2 = open_window; + +################################################################################ +# Ensure that the _NET_ACTIVE_WINDOW ClientMessage works when windows are visible +################################################################################ + +is($x->input_focus, $win2->id, 'window 2 has focus'); + +send_net_active_window($win1->id); + +is($x->input_focus, $win1->id, 'window 1 has focus'); + +################################################################################ +# Switch to a different workspace and ensure sending the _NET_ACTIVE_WINDOW +# ClientMessage has no effect anymore. +################################################################################ + +my $ws2 = fresh_workspace; +my $win3 = open_window; + +is($x->input_focus, $win3->id, 'window 3 has focus'); + +send_net_active_window($win1->id); + +is($x->input_focus, $win3->id, 'window 3 still has focus'); + +done_testing; From dbe406641fcc4fb0c55f82d10d27134c095547b7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 18:34:03 +0200 Subject: [PATCH 103/200] use errx() instead of err() for custom error message --- src/display_version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display_version.c b/src/display_version.c index 669e7c7d..7460a1a2 100644 --- a/src/display_version.c +++ b/src/display_version.c @@ -120,7 +120,7 @@ void display_running_version(void) { yajl_status state = yajl_parse(handle, (const unsigned char*)reply, (int)reply_length); if (state != yajl_status_ok) - err(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply); + errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply); printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom); printf("\n"); From e53405c216e2d1247cd68c856b84500f9546e128 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 18:40:15 +0200 Subject: [PATCH 104/200] i3bar: be less strict about the {"version":1} JSON header --- i3bar/include/common.h | 1 + i3bar/include/determine_json_version.h | 29 +++++++ i3bar/src/child.c | 13 ++-- i3bar/src/determine_json_version.c | 104 +++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 i3bar/include/determine_json_version.h create mode 100644 i3bar/src/determine_json_version.c diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 212b9dd1..59fcc44a 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -56,5 +56,6 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "xcb.h" #include "config.h" #include "libi3.h" +#include "determine_json_version.h" #endif diff --git a/i3bar/include/determine_json_version.h b/i3bar/include/determine_json_version.h new file mode 100644 index 00000000..52c6f5d2 --- /dev/null +++ b/i3bar/include/determine_json_version.h @@ -0,0 +1,29 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3bar - an xcb-based status- and ws-bar for i3 + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) + * + * determine_json_version.c: Determines the JSON protocol version based on the + * first line of input from a child program. + * + */ +#ifndef DETERMINE_JSON_VERSION_H_ +#define DETERMINE_JSON_VERSION_H_ + +#include + +/* + * Determines the JSON i3bar protocol version from the given buffer. In case + * the buffer does not contain valid JSON, or no version field is found, this + * function returns -1. The amount of bytes consumed by parsing the header is + * returned in *consumed (if non-NULL). + * + * The return type is an int32_t to avoid machines with different sizes of + * 'int' to allow different values here. It’s highly unlikely we ever exceed + * even an int8_t, but still… + * + */ +int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed); + +#endif diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 12782caf..51f5fe82 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -192,19 +192,18 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { if (first_line) { DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer); /* Detect whether this is JSON or plain text. */ - plaintext = (strncasecmp((char*)buffer, "{\"version\":", strlen("{\"version\":")) != 0); + unsigned int consumed = 0; + /* At the moment, we don’t care for the version. This might change + * in the future, but for now, we just discard it. */ + plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1); if (plaintext) { /* In case of plaintext, we just add a single block and change its * full_text pointer later. */ struct status_block *new_block = scalloc(sizeof(struct status_block)); TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks); } else { - /* At the moment, we don’t care for the version. This might change - * in the future, but for now, we just discard it. */ - while (*json_input != '\n' && *json_input != '\0') { - json_input++; - rec--; - } + json_input += consumed; + rec -= consumed; } first_line = false; } diff --git a/i3bar/src/determine_json_version.c b/i3bar/src/determine_json_version.c new file mode 100644 index 00000000..abd43038 --- /dev/null +++ b/i3bar/src/determine_json_version.c @@ -0,0 +1,104 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3bar - an xcb-based status- and ws-bar for i3 + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) + * + * determine_json_version.c: Determines the JSON protocol version based on the + * first line of input from a child program. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool version_key; +static int32_t version_number; + +#if YAJL_MAJOR >= 2 +static int version_integer(void *ctx, long long val) { +#else +static int version_integer(void *ctx, long val) { +#endif + if (version_key) + version_number = (uint32_t)val; + return 1; +} + +#if YAJL_MAJOR >= 2 +static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) { +#else +static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) { +#endif + version_key = (stringlen == strlen("version") && + strncmp((const char*)stringval, "version", strlen("version")) == 0); + return 1; +} + +static yajl_callbacks version_callbacks = { + NULL, /* null */ + NULL, /* boolean */ + &version_integer, + NULL, /* double */ + NULL, /* number */ + NULL, /* string */ + NULL, /* start_map */ + &version_map_key, + NULL, /* end_map */ + NULL, /* start_array */ + NULL /* end_array */ +}; + +/* + * Determines the JSON i3bar protocol version from the given buffer. In case + * the buffer does not contain valid JSON, or no version field is found, this + * function returns -1. The amount of bytes consumed by parsing the header is + * returned in *consumed (if non-NULL). + * + * The return type is an int32_t to avoid machines with different sizes of + * 'int' to allow different values here. It’s highly unlikely we ever exceed + * even an int8_t, but still… + * + */ +int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) { +#if YAJL_MAJOR >= 2 + yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL); + /* Allow trailing garbage. yajl 1 always behaves that way anyways, but for + * yajl 2, we need to be explicit. */ + yajl_config(handle, yajl_allow_trailing_garbage, 1); +#else + yajl_parser_config parse_conf = { 0, 0 }; + + yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL); +#endif + + version_key = false; + version_number = -1; + + yajl_status state = yajl_parse(handle, buffer, length); + if (state != yajl_status_ok) { + version_number = -1; + if (consumed != NULL) + *consumed = 0; + } else { + if (consumed != NULL) + *consumed = yajl_get_bytes_consumed(handle); + } + + yajl_free(handle); + + return version_number; +} From f94edd9fc3b5656ca197fb3f08a565b7894f4b52 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 18:47:24 +0200 Subject: [PATCH 105/200] i3bar-protocol: add example (illustration-only!) shell script, clarify {"version":1} header --- contrib/trivial-bar-script.sh | 29 +++++++++++++++++++++++++++++ docs/i3bar-protocol | 9 ++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100755 contrib/trivial-bar-script.sh diff --git a/contrib/trivial-bar-script.sh b/contrib/trivial-bar-script.sh new file mode 100755 index 00000000..15bc7dee --- /dev/null +++ b/contrib/trivial-bar-script.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# vim:ts=4:sw=4:expandtab +# © 2012 Michael Stapelberg, Public Domain + +# This script is a trivial shell script to send your own output to i3bar while +# using the JSON protocol. +# +# It is ugly and that is inherent to using JSON with shell scripts. You +# _really_ should not do that. See i3status or i3status’s contrib/ directory +# for examples of how to handle the output in higher-level languages. +# +# This example is purely for illustration of the protocol. DO NOT USE IT IN THE +# REAL WORLD. + +# Send the header so that i3bar knows we want to use JSON: +echo '{ "version": 1 }' + +# Begin the endless array. +echo '[' + +# We send an empty first array of blocks to make the loop simpler: +echo '[]' + +# Now send blocks with information forever: +while :; +do + echo ",[{\"name\":\"time\",\"full_text\":\"$(date)\"}]" + sleep 1 +done diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol index f66c7a9a..21ba9aa0 100644 --- a/docs/i3bar-protocol +++ b/docs/i3bar-protocol @@ -1,7 +1,7 @@ i3bar input protocol ==================== Michael Stapelberg -February 2012 +August 2012 This document explains the protocol in which i3bar expects its input. It provides support for colors, urgency, shortening and easy manipulation. @@ -49,6 +49,9 @@ consists of a single JSON hash: { "version": 1 } ---------------- +(Note that before i3 v4.3 the precise format had to be +{"version":1}+, +byte-for-byte.) + What follows is an infinite array (so it should be parsed by a streaming JSON parser, but as described above you can go for a simpler solution), whose elements are one array per status line. A status line is one unit of @@ -86,6 +89,10 @@ Please note that this example was pretty printed for human consumption. i3status and others will output single statuslines in one line, separated by \n. +You can find an example of a shell script which can be used as your ++status_command+ in the bar configuration at +http://code.stapelberg.de/git/i3/tree/contrib/trivial-bar-script.sh?h=next + === Blocks in detail full_text:: From 070a18e598aefdf961d2442595de648a8b082b3d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Aug 2012 19:25:48 +0200 Subject: [PATCH 106/200] i3bar: update manpage for the i3bar-protocol fixes #739 --- man/i3bar.man | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/man/i3bar.man b/man/i3bar.man index e881899b..fcefce7b 100644 --- a/man/i3bar.man +++ b/man/i3bar.man @@ -39,9 +39,8 @@ Display a short help-message and exit workspace switching buttons and a statusline generated by i3status(1) or similar. It is automatically invoked (and configured through) i3. -i3bar does not support any color or other markups, so stdin should be plain -utf8, one line at a time. If you use *i3status*(1), you therefore should -specify 'output_format = none' in the general section of its config file. +i3bar supports colors via a JSON protocol starting from v4.2, see +http://i3wm.org/docs/i3bar-protocol.html == ENVIRONMENT From e68a8dd86c34db800a2b5643c94b292ad191a708 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 00:57:57 +0200 Subject: [PATCH 107/200] shm-logging: implement i3-dump-log -f (follow) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the SHM log format, it doesn’t use 0-bytes to separate entries anymore. Instead of using lots of printf() calls in i3-dump-log, we now do precisely one big write(). So, to be clear: i3-dump-log and i3 both need to be upgraded. Mismatching versions will lead to garbage output (no crashes of i3, just garbage output). The -f flag uses an inter-process pthread_cond_t in the shared memory header to broadcast the arrival of new messages to all i3-dump-log processes. This internally uses futexes and thus doesn’t even mean a kernel call in most cases. inter-process pthread_cond_ts require NPTL (the Native Posix Thread Library, introduce in Linux 2.6). --- i3-dump-log/main.c | 97 ++++++++++++++++++++++++++++++++++------------ include/shmlog.h | 22 +++++++++++ src/i3.mk | 2 +- src/log.c | 36 ++++++++++++----- 4 files changed, 121 insertions(+), 36 deletions(-) diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c index 6b500ea3..48465c75 100644 --- a/i3-dump-log/main.c +++ b/i3-dump-log/main.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * i3-dump-log/main.c: Dumps the i3 SHM log to stdout. * @@ -28,18 +28,47 @@ #include "shmlog.h" #include +static uint32_t offset_next_write, + wrap_count; + +static i3_shmlog_header *header; +static char *logbuffer, + *walk; + +static int check_for_wrap(void) { + if (wrap_count == header->wrap_count) + return 0; + + /* The log wrapped. Print the remaining content and reset walk to the top + * of the log. */ + wrap_count = header->wrap_count; + write(STDOUT_FILENO, walk, ((logbuffer + header->offset_last_wrap) - walk)); + walk = logbuffer + sizeof(i3_shmlog_header); + return 1; +} + +static void print_till_end(void) { + check_for_wrap(); + int n = write(STDOUT_FILENO, walk, ((logbuffer + header->offset_next_write) - walk)); + if (n > 0) { + walk += n; + } +} + int main(int argc, char *argv[]) { int o, option_index = 0; - bool verbose = false; + bool verbose = false, + follow = false; static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"verbose", no_argument, 0, 'V'}, + {"follow", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; - char *options_string = "s:vVh"; + char *options_string = "s:vfVh"; while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { if (o == 'v') { @@ -47,9 +76,11 @@ int main(int argc, char *argv[]) { return 0; } else if (o == 'V') { verbose = true; + } else if (o == 'f') { + follow = true; } else if (o == 'h') { printf("i3-dump-log " I3_VERSION "\n"); - printf("i3-dump-log [-s ]\n"); + printf("i3-dump-log [-f] [-s ]\n"); return 0; } } @@ -90,45 +121,61 @@ int main(int argc, char *argv[]) { struct stat statbuf; - int logbuffer_shm = shm_open(shmname, O_RDONLY, 0); + /* NB: While we must never read, we need O_RDWR for the pthread condvar. */ + int logbuffer_shm = shm_open(shmname, O_RDWR, 0); if (logbuffer_shm == -1) err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname); if (fstat(logbuffer_shm, &statbuf) != 0) err(EXIT_FAILURE, "stat(%s)", shmname); - char *logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0); + /* NB: While we must never read, we need O_RDWR for the pthread condvar. */ + logbuffer = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0); if (logbuffer == MAP_FAILED) err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log"); - i3_shmlog_header *header = (i3_shmlog_header*)logbuffer; + header = (i3_shmlog_header*)logbuffer; if (verbose) printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n", header->offset_next_write, header->offset_last_wrap, header->size, shmname); - int chars; - char *walk = logbuffer + header->offset_next_write; - /* Skip the first line, it very likely is mangled. Not a problem, though, - * the log is chatty enough to have plenty lines left. */ - while (*walk != '\0') - walk++; + walk = logbuffer + header->offset_next_write; - /* Print the oldest log lines. We use printf("%s") to stop on \0. */ - while (walk < (logbuffer + header->offset_last_wrap)) { - chars = printf("%s", walk); - /* Shortcut: If there are two consecutive \0 bytes, this part of the - * buffer was never touched. To not call printf() for every byte of the - * buffer, we directly exit the loop. */ - if (*walk == '\0' && *(walk+1) == '\0') - break; - walk += (chars > 0 ? chars : 1); + /* We first need to print old content in case there was at least one + * wrapping already. */ + + if (*walk != '\0') { + /* In case there was a write to the buffer already, skip the first + * old line, it very likely is mangled. Not a problem, though, the log + * is chatty enough to have plenty lines left. */ + while (*walk != '\n') + walk++; + walk++; } + /* In case there was no wrapping, this is a no-op, otherwise it prints the + * old lines. */ + wrap_count = 0; + check_for_wrap(); + /* Then start from the beginning and print the newer lines */ walk = logbuffer + sizeof(i3_shmlog_header); - while (walk < (logbuffer + header->offset_next_write)) { - chars = printf("%s", walk); - walk += (chars > 0 ? chars : 1); + print_till_end(); + + if (follow) { + /* Since pthread_cond_wait() expects a mutex, we need to provide one. + * To not lock i3 (that’s bad, mhkay?) we just define one outside of + * the shared memory. */ + pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&dummy_mutex); + while (1) { + pthread_cond_wait(&(header->condvar), &dummy_mutex); + /* If this was not a spurious wakeup, print the new lines. */ + if (header->offset_next_write != offset_next_write) { + offset_next_write = header->offset_next_write; + print_till_end(); + } + } } return 0; diff --git a/include/shmlog.h b/include/shmlog.h index c513babf..e755d2f1 100644 --- a/include/shmlog.h +++ b/include/shmlog.h @@ -12,11 +12,33 @@ #define _I3_SHMLOG_H #include +#include +/* + * Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c. + * + */ typedef struct i3_shmlog_header { + /* Byte offset where the next line will be written to. */ uint32_t offset_next_write; + + /* Byte offset where the last wrap occured. */ uint32_t offset_last_wrap; + + /* The size of the logfile in bytes. Since the size is limited to 25 MiB + * an uint32_t is sufficient. */ uint32_t size; + + /* wrap counter. We need it to reliably signal to clients that we just + * wrapped (clients cannot use offset_last_wrap because that might + * coincidentally be exactly the same as previously). Overflows can happen + * and don’t matter — clients use an equality check (==). */ + uint32_t wrap_count; + + /* pthread condvar which will be broadcasted whenever there is a new + * message in the log. i3-dump-log uses this to implement -f (follow, like + * tail -f) in an efficient way. */ + pthread_cond_t condvar; } i3_shmlog_header; #endif diff --git a/src/i3.mk b/src/i3.mk index f56a60a4..8ba04cd2 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -7,7 +7,7 @@ i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm +i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread # When using clang, we use pre-compiled headers to speed up the build. With # gcc, this actually makes the build slower. diff --git a/src/log.c b/src/log.c index ab75395f..16fa0bed 100644 --- a/src/log.c +++ b/src/log.c @@ -20,6 +20,7 @@ #include #include #include +#include #if defined(__APPLE__) #include #include @@ -48,6 +49,8 @@ int shmlog_size = 0; static char *logbuffer; /* A pointer (within logbuffer) where data will be written to next. */ static char *logwalk; +/* A pointer to the shmlog header */ +static i3_shmlog_header *header; /* A pointer to the byte where we last wrapped. Necessary to not print the * left-overs at the end of the ringbuffer. */ static char *loglastwrap; @@ -63,8 +66,6 @@ static int logbuffer_shm; * */ static void store_log_markers(void) { - i3_shmlog_header *header = (i3_shmlog_header*)logbuffer; - header->offset_next_write = (logwalk - logbuffer); header->offset_last_wrap = (loglastwrap - logbuffer); header->size = logbuffer_size; @@ -128,6 +129,18 @@ void init_logging(void) { logbuffer = NULL; return; } + + /* Initialize with 0-bytes, just to be sure… */ + memset(logbuffer, '\0', logbuffer_size); + + header = (i3_shmlog_header*)logbuffer; + + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0) + ELOG("pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n"); + pthread_cond_init(&(header->condvar), &cond_attr); + logwalk = logbuffer + sizeof(i3_shmlog_header); loglastwrap = logbuffer + logbuffer_size; store_log_markers(); @@ -199,22 +212,25 @@ static void vlog(const bool print, const char *fmt, va_list args) { fprintf(stderr, "BUG: single log message > 4k\n"); } - /* If there is no space for the current message (plus trailing - * nullbyte) in the ringbuffer, we need to wrap and write to the - * beginning again. */ - if ((len+1) >= (logbuffer_size - (logwalk - logbuffer))) { + /* If there is no space for the current message in the ringbuffer, we + * need to wrap and write to the beginning again. */ + if (len >= (logbuffer_size - (logwalk - logbuffer))) { loglastwrap = logwalk; logwalk = logbuffer + sizeof(i3_shmlog_header); + store_log_markers(); + header->wrap_count++; } - /* Copy the buffer, terminate it, move the write pointer to the byte after - * our current message. */ + /* Copy the buffer, move the write pointer to the byte after our + * current message. */ strncpy(logwalk, message, len); - logwalk[len] = '\0'; - logwalk += len + 1; + logwalk += len; store_log_markers(); + /* Wake up all (i3-dump-log) processes waiting for condvar. */ + pthread_cond_broadcast(&(header->condvar)); + if (print) fwrite(message, len, 1, stdout); } From 4eff386439ffc589b4a86ac995a4ee508fd26f65 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 01:57:23 +0200 Subject: [PATCH 108/200] remove obsolete branch reference "(tree)" from starting line --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 47d66b87..cc83851c 100644 --- a/src/main.c +++ b/src/main.c @@ -494,7 +494,7 @@ int main(int argc, char *argv[]) { } } - LOG("i3 (tree) version " I3_VERSION " starting\n"); + LOG("i3 " I3_VERSION " starting\n"); conn = xcb_connect(NULL, &conn_screen); if (xcb_connection_has_error(conn)) From a7569e6a98e0bd08cf74b838ab3cd90be35df771 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 01:57:39 +0200 Subject: [PATCH 109/200] implement error messages when moving to another ws fails (Thanks eeemsi) fixes #769 --- src/commands.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/commands.c b/src/commands.c index 02d602f0..728a1d6c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -416,8 +416,14 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { /* We have nothing to move: * when criteria was specified but didn't match any window or * when criteria wasn't specified and we don't have any window focused. */ - if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || - (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { + if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) { + ELOG("No windows match your criteria, cannot move.\n"); + ysuccess(false); + return; + } + + if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) { + ELOG("No window to move, you have focused a workspace.\n"); ysuccess(false); return; } From b01545b131017224952b409df32c7ae40fdb6f69 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 12 Aug 2012 00:59:19 +0200 Subject: [PATCH 110/200] Makefile: Always link libi3 first --- i3-config-wizard/i3-config-wizard.mk | 2 +- i3-input/i3-input.mk | 2 +- i3-nagbar/i3-nagbar.mk | 2 +- i3bar/i3bar.mk | 2 +- src/i3.mk | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index bce679df..526a4ecd 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -25,7 +25,7 @@ i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_ i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS) echo "[i3-config-wizard] Link i3-config-wizard" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_config_wizard_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_config_wizard_LIBS) install-i3-config-wizard: i3-config-wizard/i3-config-wizard echo "[i3-config-wizard] Install" diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 9fb4b194..98131e76 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -16,7 +16,7 @@ i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) i3-input/i3-input: libi3.a $(i3_input_OBJECTS) echo "[i3-input] Link i3-input" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_input_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_input_LIBS) install-i3-input: i3-input/i3-input echo "[i3-input] Install" diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index f54d8380..5e5f2c4d 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -16,7 +16,7 @@ i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) echo "[i3-nagbar] Link i3-nagbar" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_nagbar_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_nagbar_LIBS) install-i3-nagbar: i3-nagbar/i3-nagbar echo "[i3-nagbar] Install" diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index cf3bf346..5b4ddd80 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -16,7 +16,7 @@ i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) i3bar/i3bar: libi3.a $(i3bar_OBJECTS) echo "[i3bar] Link i3bar" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3bar_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3bar_LIBS) install-i3bar: i3bar/i3bar echo "[i3bar] Install" diff --git a/src/i3.mk b/src/i3.mk index 8ba04cd2..9591e178 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -50,7 +50,7 @@ src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS_DEP) # and once as an object file for i3. src/commands_parser.o: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS) + $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) $(i3_LIBS) $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec @@ -60,7 +60,7 @@ i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec i3: libi3.a $(i3_OBJECTS) echo "[i3] Link i3" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_LIBS) $(LIBS) + $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_LIBS) install-i3: i3 echo "[i3] Install" From 7f22d4fe3279c4945cb42cc3318af84ed71be575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sun, 5 Aug 2012 23:01:50 +0200 Subject: [PATCH 111/200] libi3: Implement i3String New type designed to handle UCS-2/UTF-8 conversion nicely --- include/libi3.h | 71 ++++++++++++++++++++++++ libi3/string.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 libi3/string.c diff --git a/include/libi3.h b/include/libi3.h index 2126e100..97383ba3 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -18,6 +18,12 @@ #include #include +/** + * Opaque data structure for storing strings. + * + */ +typedef struct _i3String i3String; + typedef struct Font i3Font; /** @@ -91,6 +97,71 @@ char *sstrdup(const char *str); */ int sasprintf(char **strp, const char *fmt, ...); +/** + * Build an i3String from an UTF-8 encoded string. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_utf8(const char *from_utf8); + +/** + * Build an i3String from an UTF-8 encoded string with fixed length. + * To be used when no proper NUL-terminaison is available. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes); + +/** + * Build an i3String from an UCS-2 encoded string. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs); + +/** + * Free an i3String. + * + */ +void i3string_free(i3String *str); + +/** + * Securely i3string_free by setting the pointer to NULL + * to prevent accidentally using freed memory. + * + */ +#define I3STRING_FREE(str) \ +do { \ + if (str != NULL) { \ + i3string_free(str); \ + str = NULL; \ + } \ +} while (0) + +/** + * Returns the UTF-8 encoded version of the i3String. + * + */ +const char *i3string_as_utf8(i3String *str); + +/** + * Returns the UCS-2 encoded version of the i3String. + * + */ +const xcb_char2b_t *i3string_as_ucs2(i3String *str); + +/** + * Returns the number of bytes (UTF-8 encoded) in an i3String. + * + */ +size_t i3string_get_num_bytes(i3String *str); + +/** + * Returns the number of glyphs in an i3String. + * + */ +size_t i3string_get_num_glyphs(i3String *str); + /** * Connects to the i3 IPC socket and returns the file descriptor for the * socket. die()s if anything goes wrong. diff --git a/libi3/string.c b/libi3/string.c new file mode 100644 index 00000000..009312d6 --- /dev/null +++ b/libi3/string.c @@ -0,0 +1,143 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * + * string.c: Define an i3String type to automagically handle UTF-8/UCS-2 + * conversions. Some font backends need UCS-2 (X core fonts), + * others want UTF-8 (Pango). + * + */ + +#include +#include + +#include "libi3.h" + +struct _i3String { + char *utf8; + xcb_char2b_t *ucs2; + size_t num_glyphs; + size_t num_bytes; +}; + +/* + * Build an i3String from an UTF-8 encoded string. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_utf8(const char *from_utf8) { + i3String *str = scalloc(sizeof(i3String)); + + /* Get the text */ + str->utf8 = sstrdup(from_utf8); + + /* Compute and store the length */ + str->num_bytes = strlen(str->utf8); + + return str; +} + +/* + * Build an i3String from an UTF-8 encoded string with fixed length. + * To be used when no proper NUL-terminaison is available. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes) { + i3String *str = scalloc(sizeof(i3String)); + + /* Copy the actual text to our i3String */ + str->utf8 = scalloc(sizeof(char) * (num_bytes + 1)); + strncpy(str->utf8, from_utf8, num_bytes); + str->utf8[num_bytes] = '\0'; + + /* Store the length */ + str->num_bytes = num_bytes; + + return str; +} + +/* + * Build an i3String from an UCS-2 encoded string. + * Returns the newly-allocated i3String. + * + */ +i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) { + i3String *str = scalloc(sizeof(i3String)); + + /* Copy the actual text to our i3String */ + size_t num_bytes = num_glyphs * sizeof(xcb_char2b_t); + str->ucs2 = scalloc(num_bytes); + memcpy(str->ucs2, from_ucs2, num_bytes); + + /* Store the length */ + str->num_glyphs = num_glyphs; + + str->utf8 = NULL; + str->num_bytes = 0; + + return str; +} + +/* + * Free an i3String. + * + */ +void i3string_free(i3String *str) { + if (str == NULL) + return; + free(str->utf8); + free(str->ucs2); + free(str); +} + +static void i3string_ensure_utf8(i3String *str) { + if (str->utf8 != NULL) + return; + if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL) + str->num_bytes = strlen(str->utf8); +} + +static void i3string_ensure_ucs2(i3String *str) { + if (str->ucs2 != NULL) + return; + str->ucs2 = convert_utf8_to_ucs2(str->utf8, &str->num_glyphs); +} + +/* + * Returns the UTF-8 encoded version of the i3String. + * + */ +const char *i3string_as_utf8(i3String *str) { + i3string_ensure_utf8(str); + return str->utf8; +} + +/* + * Returns the UCS-2 encoded version of the i3String. + * + */ +const xcb_char2b_t *i3string_as_ucs2(i3String *str) { + i3string_ensure_ucs2(str); + return str->ucs2; +} + +/* + * Returns the number of bytes (UTF-8 encoded) in an i3String. + * + */ +size_t i3string_get_num_bytes(i3String *str) { + i3string_ensure_utf8(str); + return str->num_bytes; +} + +/* + * Returns the number of glyphs in an i3String. + * + */ +size_t i3string_get_num_glyphs(i3String *str) { + i3string_ensure_ucs2(str); + return str->num_glyphs; +} From 50d52f8f9b160bd83c6ae3922128e980b223cfbe Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Fri, 10 Aug 2012 15:39:50 +0200 Subject: [PATCH 112/200] i3bar/util.h: Prepare for libi3.h inclusion --- i3bar/include/util.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/i3bar/include/util.h b/i3bar/include/util.h index eb05ed08..262d7750 100644 --- a/i3bar/include/util.h +++ b/i3bar/include/util.h @@ -51,6 +51,11 @@ } \ } while(0) +/* We will include libi3.h which define its own version of ELOG. + * We want *our* version, so we undef the libi3 one. */ +#if defined(ELOG) +#undef ELOG +#endif #define ELOG(fmt, ...) do { \ fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ } while(0) From bbd1b1604359087055a9a5066cbeab32b200b418 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 19:46:23 +0200 Subject: [PATCH 113/200] i3bar: Port to i3String --- i3bar/include/common.h | 8 ++------ i3bar/include/workspaces.h | 4 +--- i3bar/src/child.c | 10 +++++----- i3bar/src/workspaces.c | 21 +++++++-------------- i3bar/src/xcb.c | 31 ++++++++++++++----------------- 5 files changed, 29 insertions(+), 45 deletions(-) diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 59fcc44a..6f8a7b2d 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -11,6 +11,7 @@ #include #include #include +#include "libi3.h" #include "queue.h" typedef struct rect_t rect; @@ -29,15 +30,10 @@ struct rect_t { /* This data structure represents one JSON dictionary, multiple of these make * up one status line. */ struct status_block { - char *full_text; + i3String *full_text; char *color; - /* full_text, but converted to UCS-2. This variable is only temporarily - * used in refresh_statusline(). */ - xcb_char2b_t *ucs2_full_text; - size_t glyph_count_full_text; - /* The amount of pixels necessary to render this block. This variable is * only temporarily used in refresh_statusline(). */ uint32_t width; diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h index dfd93d9b..1c77b0b3 100644 --- a/i3bar/include/workspaces.h +++ b/i3bar/include/workspaces.h @@ -32,9 +32,7 @@ void free_workspaces(); struct i3_ws { int num; /* The internal number of the ws */ - char *name; /* The name (in utf8) of the ws */ - xcb_char2b_t *ucs2_name; /* The name (in ucs2) of the ws */ - int name_glyphs; /* The length (in glyphs) of the name */ + i3String *name; /* The name of the ws */ int name_width; /* The rendered width of the name */ bool visible; /* If the ws is currently visible on an output */ bool focused; /* If the ws is currently focused */ diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 51f5fe82..5cbae6a5 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -116,7 +116,7 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le #endif parser_ctx *ctx = context; if (strcasecmp(ctx->last_map_key, "full_text") == 0) { - sasprintf(&(ctx->block.full_text), "%.*s", len, val); + ctx->block.full_text = i3string_from_utf8_with_length((const char *)val, len); } if (strcasecmp(ctx->last_map_key, "color") == 0) { sasprintf(&(ctx->block.color), "%.*s", len, val); @@ -131,7 +131,7 @@ static int stdin_end_map(void *context) { /* Ensure we have a full_text set, so that when it is missing (or null), * i3bar doesn’t crash and the user gets an annoying message. */ if (!new_block->full_text) - new_block->full_text = sstrdup("SPEC VIOLATION (null)"); + new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)"); TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks); return 1; } @@ -140,7 +140,7 @@ static int stdin_end_array(void *context) { DLOG("dumping statusline:\n"); struct status_block *current; TAILQ_FOREACH(current, &statusline_head, blocks) { - DLOG("full_text = %s\n", current->full_text); + DLOG("full_text = %s\n", i3string_as_utf8(current->full_text)); DLOG("color = %s\n", current->color); } DLOG("end of dump\n"); @@ -221,13 +221,13 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { } else { struct status_block *first = TAILQ_FIRST(&statusline_head); /* Clear the old buffer if any. */ - FREE(first->full_text); + I3STRING_FREE(first->full_text); /* Remove the trailing newline and terminate the string at the same * time. */ if (buffer[rec-1] == '\n' || buffer[rec-1] == '\r') buffer[rec-1] = '\0'; else buffer[rec] = '\0'; - first->full_text = (char*)buffer; + first->full_text = i3string_from_utf8((const char *)buffer); } draw_bars(); } diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index 5df1899f..c77103f3 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -114,23 +114,17 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne if (!strcmp(params->cur_key, "name")) { /* Save the name */ - params->workspaces_walk->name = smalloc(sizeof(const unsigned char) * (len + 1)); - strncpy(params->workspaces_walk->name, (const char*) val, len); - params->workspaces_walk->name[len] = '\0'; + params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len); /* Convert the name to ucs2, save its length in glyphs and calculate its rendered width */ - size_t ucs2_len; - xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len); - params->workspaces_walk->ucs2_name = ucs2_name; - params->workspaces_walk->name_glyphs = ucs2_len; params->workspaces_walk->name_width = - predict_text_width((char *)params->workspaces_walk->ucs2_name, - params->workspaces_walk->name_glyphs, true); + predict_text_width((char *)i3string_as_ucs2(params->workspaces_walk->name), + i3string_get_num_glyphs(params->workspaces_walk->name), true); - DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n", - params->workspaces_walk->name, + DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n", + i3string_as_utf8(params->workspaces_walk->name), params->workspaces_walk->name_width, - params->workspaces_walk->name_glyphs); + i3string_get_num_glyphs(params->workspaces_walk->name)); FREE(params->cur_key); return 1; @@ -279,8 +273,7 @@ void free_workspaces() { SLIST_FOREACH(outputs_walk, outputs, slist) { if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) { TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { - FREE(ws_walk->name); - FREE(ws_walk->ucs2_name); + I3STRING_FREE(ws_walk->name); } FREE_TAILQ(outputs_walk->workspaces, i3_ws); } diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 419a4723..be3ca35a 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -114,14 +114,12 @@ void refresh_statusline() { uint32_t old_statusline_width = statusline_width; statusline_width = 0; - /* Convert all blocks from UTF-8 to UCS-2 and predict the text width (in - * pixels). */ + /* Predict the text width of all blocks (in pixels). */ TAILQ_FOREACH(block, &statusline_head, blocks) { - if (strlen(block->full_text) == 0) + if (i3string_get_num_bytes(block->full_text) == 0) continue; - block->ucs2_full_text = (xcb_char2b_t*)convert_utf8_to_ucs2(block->full_text, &(block->glyph_count_full_text)); - block->width = predict_text_width((char*)block->ucs2_full_text, block->glyph_count_full_text, true); + block->width = predict_text_width((char *)i3string_as_ucs2(block->full_text), i3string_get_num_glyphs(block->full_text), true); /* If this is not the last block, add some pixels for a separator. */ if (TAILQ_NEXT(block, blocks) != NULL) block->width += 9; @@ -141,12 +139,12 @@ void refresh_statusline() { /* Draw the text of each block. */ uint32_t x = 0; TAILQ_FOREACH(block, &statusline_head, blocks) { - if (strlen(block->full_text) == 0) + if (i3string_get_num_bytes(block->full_text) == 0) continue; uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); - draw_text((char*)block->ucs2_full_text, block->glyph_count_full_text, + draw_text((char *)i3string_as_ucs2(block->full_text), i3string_get_num_glyphs(block->full_text), true, statusline_pm, statusline_ctx, x, 0, block->width); x += block->width; @@ -157,8 +155,6 @@ void refresh_statusline() { statusline_ctx, 2, (xcb_point_t[]){ { x - 5, 2 }, { x - 5, font.height - 2 } }); } - - FREE(block->ucs2_full_text); } } @@ -326,7 +322,8 @@ void handle_button(xcb_button_press_event_t *event) { * buffer, then we copy character by character. */ int num_quotes = 0; size_t namelen = 0; - for (char *walk = cur_ws->name; *walk != '\0'; walk++) { + const char *utf8_name = i3string_as_utf8(cur_ws->name); + for (const char *walk = utf8_name; *walk != '\0'; walk++) { if (*walk == '"') num_quotes++; /* While we’re looping through the name anyway, we can save one @@ -341,11 +338,11 @@ void handle_button(xcb_button_press_event_t *event) { for (inpos = 0, outpos = strlen("workspace \""); inpos < namelen; inpos++, outpos++) { - if (cur_ws->name[inpos] == '"') { + if (utf8_name[inpos] == '"') { buffer[outpos] = '\\'; outpos++; } - buffer[outpos] = cur_ws->name[inpos]; + buffer[outpos] = utf8_name[inpos]; } buffer[outpos] = '"'; i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer); @@ -1468,7 +1465,7 @@ void draw_bars() { bool has_urgent = false, walks_away = true; TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { - DLOG("Drawing Button for WS %s at x = %d, len = %d\n", ws_walk->name, i, ws_walk->name_width); + DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width); uint32_t fg_color = colors.inactive_ws_fg; uint32_t bg_color = colors.inactive_ws_bg; uint32_t border_color = colors.inactive_ws_border; @@ -1481,19 +1478,19 @@ void draw_bars() { fg_color = colors.focus_ws_fg; bg_color = colors.focus_ws_bg; border_color = colors.focus_ws_border; - if (last_urgent_ws && strcmp(ws_walk->name, last_urgent_ws) == 0) + if (last_urgent_ws && strcmp(i3string_as_utf8(ws_walk->name), last_urgent_ws) == 0) walks_away = false; } } if (ws_walk->urgent) { - DLOG("WS %s is urgent!\n", ws_walk->name); + DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name)); fg_color = colors.urgent_ws_fg; bg_color = colors.urgent_ws_bg; border_color = colors.urgent_ws_border; has_urgent = true; if (!ws_walk->focused) { FREE(last_urgent_ws); - last_urgent_ws = sstrdup(ws_walk->name); + last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name)); } /* The urgent-hint should get noticed, so we unhide the bars shortly */ unhide_bars(); @@ -1522,7 +1519,7 @@ void draw_bars() { 1, &rect); set_font_colors(outputs_walk->bargc, fg_color, bg_color); - draw_text((char*)ws_walk->ucs2_name, ws_walk->name_glyphs, true, + draw_text((char*)i3string_as_ucs2(ws_walk->name), i3string_get_num_glyphs(ws_walk->name), true, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width); i += 10 + ws_walk->name_width + 1; } From 50b7764ae4c62ac15327e2ec263588818a23d53e Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 19:58:55 +0200 Subject: [PATCH 114/200] i3-nagbar: Port to i3String --- i3-nagbar/main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 87214477..7a09b99d 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -30,7 +30,7 @@ #include "i3-nagbar.h" typedef struct { - char *label; + i3String *label; char *action; int16_t x; uint16_t width; @@ -41,7 +41,7 @@ static xcb_pixmap_t pixmap; static xcb_gcontext_t pixmap_gc; static xcb_rectangle_t rect = { 0, 0, 600, 20 }; static i3Font font; -static char *prompt; +static i3String *prompt; static button_t *buttons; static int buttoncnt; @@ -132,7 +132,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { /* restore font color */ set_font_colors(pixmap_gc, color_text, color_background); - draw_text(prompt, strlen(prompt), false, pixmap, pixmap_gc, + draw_text((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true, pixmap, pixmap_gc, 4 + 4, 4 + 4, rect.width - 4 - 4); /* render close button */ @@ -190,7 +190,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { values[0] = color_text; values[1] = color_button_background; set_font_colors(pixmap_gc, color_text, color_button_background); - draw_text(buttons[c].label, strlen(buttons[c].label), false, pixmap, pixmap_gc, + draw_text((char *)i3string_as_ucs2(buttons[c].label), i3string_get_num_glyphs(buttons[c].label), true, pixmap, pixmap_gc, y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6); y -= w; @@ -232,7 +232,7 @@ int main(int argc, char *argv[]) { char *options_string = "b:f:m:t:vh"; - prompt = sstrdup("Please do not run this program."); + prompt = i3string_from_utf8("Please do not run this program."); while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { switch (o) { @@ -244,8 +244,8 @@ int main(int argc, char *argv[]) { pattern = sstrdup(optarg); break; case 'm': - FREE(prompt); - prompt = sstrdup(optarg); + i3string_free(prompt); + prompt = i3string_from_utf8(optarg); break; case 't': bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR); @@ -256,10 +256,10 @@ int main(int argc, char *argv[]) { return 0; case 'b': buttons = realloc(buttons, sizeof(button_t) * (buttoncnt + 1)); - buttons[buttoncnt].label = optarg; + buttons[buttoncnt].label = i3string_from_utf8(optarg); buttons[buttoncnt].action = argv[optind]; printf("button with label *%s* and action *%s*\n", - buttons[buttoncnt].label, + i3string_as_utf8(buttons[buttoncnt].label), buttons[buttoncnt].action); buttoncnt++; printf("now %d buttons\n", buttoncnt); From bc5f33878a256b3268411d110718bb2ff1b66862 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Fri, 10 Aug 2012 15:39:50 +0200 Subject: [PATCH 115/200] i3/log.h: Prepare for libi3.h inclusion --- include/log.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/log.h b/include/log.h index 0854628d..26e85f04 100644 --- a/include/log.h +++ b/include/log.h @@ -16,6 +16,11 @@ /** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that is, delete the preceding comma */ #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) +/* We will include libi3.h which define its own version of ELOG. + * We want *our* version, so we undef the libi3 one. */ +#if defined(ELOG) +#undef ELOG +#endif #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__) #define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) From b6c705a1a4eed6a885d6bbed8246b53d48e60819 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 21:23:06 +0200 Subject: [PATCH 116/200] i3/window: Port window names to i3String --- include/data.h | 13 +++---------- src/ipc.c | 4 ++-- src/match.c | 6 +++--- src/tree.c | 3 +-- src/window.c | 46 ++++++++-------------------------------------- src/x.c | 4 ++-- 6 files changed, 19 insertions(+), 57 deletions(-) diff --git a/include/data.h b/include/data.h index a5ac943c..6df3f6fc 100644 --- a/include/data.h +++ b/include/data.h @@ -19,6 +19,7 @@ #include #include +#include "libi3.h" #include "queue.h" /* @@ -287,9 +288,8 @@ struct Window { char *class_class; char *class_instance; - /** The name of the window as it will be passed to X11 (in UCS2 if the - * application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */ - char *name_x; + /** The name of the window. */ + i3String *name; /** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window * sets "buddy list"). Useful to match specific windows in assignments or @@ -299,13 +299,6 @@ struct Window { /** Flag to force re-rendering the decoration upon changes */ bool name_x_changed; - /** The name of the window as used in JSON (in UTF-8 if the application - * supports _NET_WM_NAME, in COMPOUND_TEXT otherwise) */ - char *name_json; - - /** The length of the name in glyphs (not bytes) */ - size_t name_len; - /** Whether the application used _NET_WM_NAME */ bool uses_net_wm_name; diff --git a/src/ipc.c b/src/ipc.c index 8db69259..1c6de798 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -259,8 +259,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { dump_rect(gen, "geometry", con->geometry); ystr("name"); - if (con->window && con->window->name_json) - ystr(con->window->name_json); + if (con->window && con->window->name) + ystr(i3string_as_utf8(con->window->name)); else ystr(con->name); diff --git a/src/match.c b/src/match.c index 1014de84..350a2c11 100644 --- a/src/match.c +++ b/src/match.c @@ -113,9 +113,9 @@ bool match_matches_window(Match *match, i3Window *window) { } if (match->title != NULL) { - if (window->name_json != NULL && - regex_matches(match->title, window->name_json)) { - LOG("title matches (%s)\n", window->name_json); + if (window->name != NULL && + regex_matches(match->title, i3string_as_utf8(window->name))) { + LOG("title matches (%s)\n", i3string_as_utf8(window->name)); } else { return false; } diff --git a/src/tree.c b/src/tree.c index d2dc10ea..2c1c257e 100644 --- a/src/tree.c +++ b/src/tree.c @@ -247,8 +247,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool } FREE(con->window->class_class); FREE(con->window->class_instance); - FREE(con->window->name_x); - FREE(con->window->name_json); + i3string_free(con->window->name); free(con->window); } diff --git a/src/window.c b/src/window.c index 6ec63a8b..b886c380 100644 --- a/src/window.c +++ b/src/window.c @@ -60,31 +60,11 @@ void window_update_name(i3Window *win, xcb_get_property_reply_t *prop, bool befo return; } - /* Save the old pointer to make the update atomic */ - char *new_name; - if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), - (char*)xcb_get_property_value(prop)) == -1) { - perror("asprintf()"); - DLOG("Could not get window name\n"); - free(prop); - return; - } - /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */ - size_t len; - xcb_char2b_t *ucs2_name = convert_utf8_to_ucs2(new_name, &len); - if (ucs2_name == NULL) { - LOG("Could not convert _NET_WM_NAME to UCS-2, ignoring new hint\n"); - FREE(new_name); - free(prop); - return; - } - FREE(win->name_x); - FREE(win->name_json); - win->name_json = new_name; - win->name_x = (char*)ucs2_name; - win->name_len = len; + i3string_free(win->name); + win->name = i3string_from_utf8_with_length(xcb_get_property_value(prop), + xcb_get_property_value_length(prop)); win->name_x_changed = true; - LOG("_NET_WM_NAME changed to \"%s\"\n", win->name_json); + LOG("_NET_WM_NAME changed to \"%s\"\n", i3string_as_utf8(win->name)); win->uses_net_wm_name = true; @@ -118,24 +98,14 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bo return; } - char *new_name; - if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), - (char*)xcb_get_property_value(prop)) == -1) { - perror("asprintf()"); - DLOG("Could not get legacy window name\n"); - free(prop); - return; - } + i3string_free(win->name); + win->name = i3string_from_utf8_with_length(xcb_get_property_value(prop), + xcb_get_property_value_length(prop)); - LOG("WM_NAME changed to \"%s\"\n", new_name); + LOG("WM_NAME changed to \"%s\"\n", i3string_as_utf8(win->name)); LOG("Using legacy window title. Note that in order to get Unicode window " "titles in i3, the application has to set _NET_WM_NAME (UTF-8)\n"); - FREE(win->name_x); - FREE(win->name_json); - win->name_x = new_name; - win->name_json = sstrdup(new_name); - win->name_len = strlen(new_name); win->name_x_changed = true; if (before_mgmt) { diff --git a/src/x.c b/src/x.c index 84217a85..864949e5 100644 --- a/src/x.c +++ b/src/x.c @@ -481,7 +481,7 @@ void x_draw_decoration(Con *con) { int text_offset_y = (con->deco_rect.height - config.font.height) / 2; struct Window *win = con->window; - if (win == NULL || win->name_x == NULL) { + if (win == NULL || win->name == NULL) { /* this is a non-leaf container, we need to make up a good description */ // TODO: use a good description instead of just "another container" draw_text("another container", strlen("another container"), false, @@ -508,7 +508,7 @@ void x_draw_decoration(Con *con) { //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult); int indent_px = (indent_level * 5) * indent_mult; - draw_text(win->name_x, win->name_len, win->uses_net_wm_name, + draw_text((char *)i3string_as_ucs2(win->name), i3string_get_num_glyphs(win->name), true, parent->pixmap, parent->pm_gc, con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, con->deco_rect.width - 2 - indent_px); From bf177da17db80fdc6ff8f86f29d218a350af24bb Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 22:30:37 +0200 Subject: [PATCH 117/200] i3/sighandler: Pre-compute i3Strings for text --- src/sighandler.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sighandler.c b/src/sighandler.c index bae73c16..2c53333d 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -39,7 +39,7 @@ static int crash_text_longest = 5; * Draw the window containing the info text * */ -static int sig_draw_window(xcb_window_t win, int width, int height, int font_height) { +static int sig_draw_window(xcb_window_t win, int width, int height, int font_height, i3String **crash_text_i3strings) { /* re-draw the background */ xcb_rectangle_t border = { 0, 0, width, height}, inner = { 2, 2, width - 4, height - 4}; @@ -51,8 +51,8 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei /* restore font color */ set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); - for (int i = 0; i < sizeof(crash_text) / sizeof(char*); i++) { - draw_text(crash_text[i], strlen(crash_text[i]), false, pixmap, pixmap_gc, + for (int i = 0; crash_text_i3strings[i] != NULL; ++i) { + draw_text((char *)i3string_as_ucs2(crash_text_i3strings[i]), i3string_get_num_glyphs(crash_text_i3strings[i]), true, pixmap, pixmap_gc, 8, 5 + i * font_height, width - 16); } @@ -147,10 +147,15 @@ void handle_signal(int sig, siginfo_t *info, void *data) { int crash_text_num = sizeof(crash_text) / sizeof(char*); int height = 13 + (crash_text_num * config.font.height); + int crash_text_length = sizeof(crash_text) / sizeof(char*); + i3String **crash_text_i3strings = smalloc(sizeof(i3String *) * (crash_text_length + 1)); + /* Pre-compute i3Strings for our text */ + for (int i = 0; i < crash_text_length; ++i) { + crash_text_i3strings[i] = i3string_from_utf8(crash_text[i]); + } + crash_text_i3strings[crash_text_length] = NULL; /* calculate width for longest text */ - size_t text_len = strlen(crash_text[crash_text_longest]); - xcb_char2b_t *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len); - int font_width = predict_text_width((char *)longest_text, text_len, true); + int font_width = predict_text_width((char *)i3string_as_ucs2(crash_text_i3strings[crash_text_longest]), i3string_get_num_glyphs(crash_text_i3strings[crash_text_longest]), true); int width = font_width + 20; /* Open a popup window on each virtual screen */ @@ -174,7 +179,7 @@ void handle_signal(int sig, siginfo_t *info, void *data) { xcb_grab_pointer(conn, false, win, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, win, XCB_NONE, XCB_CURRENT_TIME); - sig_draw_window(win, width, height, config.font.height); + sig_draw_window(win, width, height, config.font.height, crash_text_i3strings); xcb_flush(conn); } From d89cb04c980120d46d6e0b569f48da3507326975 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 22:10:30 +0200 Subject: [PATCH 118/200] i3-input: Port prompt to i3String --- i3-input/main.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index 7e236ca7..0ec879f1 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -49,8 +49,8 @@ static char *glyphs_ucs[512]; static char *glyphs_utf8[512]; static int input_position; static i3Font font; -static char *prompt; -static size_t prompt_len; +static i3String *prompt; +static int prompt_offset = 0; static int limit; xcb_window_t root; xcb_connection_t *conn; @@ -96,25 +96,21 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t /* restore font color */ set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); - /* draw the text */ - uint8_t *con = concat_strings(glyphs_ucs, input_position); - char *full_text = (char*)con; + /* draw the prompt … */ if (prompt != NULL) { - full_text = malloc((prompt_len + input_position) * 2 + 1); - if (full_text == NULL) - err(EXIT_FAILURE, "malloc() failed\n"); - memcpy(full_text, prompt, prompt_len * 2); - memcpy(full_text + (prompt_len * 2), con, input_position * 2); + draw_text((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true, pixmap, pixmap_gc, 4, 4, 492); + } + /* … and the text */ + if (input_position > 0) + { + char *full_text = (char *)concat_strings(glyphs_ucs, input_position); + draw_text(full_text, input_position, true, pixmap, pixmap_gc, 4, 4 + prompt_offset, 492 - prompt_offset); + free(full_text); } - if (input_position + prompt_len != 0) - draw_text(full_text, input_position + prompt_len, true, pixmap, pixmap_gc, 4, 4, 492); /* Copy the contents of the pixmap to the real window */ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8); xcb_flush(conn); - free(con); - if (prompt != NULL) - free(full_text); return 1; } @@ -308,8 +304,8 @@ int main(int argc, char *argv[]) { limit = atoi(optarg); break; case 'P': - FREE(prompt); - prompt = strdup(optarg); + i3string_free(prompt); + prompt = i3string_from_utf8(optarg); break; case 'f': FREE(pattern); @@ -340,7 +336,7 @@ int main(int argc, char *argv[]) { sockfd = ipc_connect(socket_path); if (prompt != NULL) - prompt = (char*)convert_utf8_to_ucs2(prompt, &prompt_len); + prompt_offset = predict_text_width((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true); int screens; conn = xcb_connect(NULL, &screens); From 284294e9c276d2948fae9a52ccebfc69f76514a8 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Wed, 8 Aug 2012 15:08:05 +0200 Subject: [PATCH 119/200] i3-input: Store and use xcb_char2b_t directly --- i3-input/main.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index 0ec879f1..9ab40aec 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -45,7 +45,7 @@ static bool modeswitch_active = false; static xcb_window_t win; static xcb_pixmap_t pixmap; static xcb_gcontext_t pixmap_gc; -static char *glyphs_ucs[512]; +static xcb_char2b_t glyphs_ucs[512]; static char *glyphs_utf8[512]; static int input_position; static i3Font font; @@ -102,11 +102,7 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t } /* … and the text */ if (input_position > 0) - { - char *full_text = (char *)concat_strings(glyphs_ucs, input_position); - draw_text(full_text, input_position, true, pixmap, pixmap_gc, 4, 4 + prompt_offset, 492 - prompt_offset); - free(full_text); - } + draw_text((char *)glyphs_ucs, input_position, true, pixmap, pixmap_gc, 4, 4 + prompt_offset, 492 - prompt_offset); /* Copy the contents of the pixmap to the real window */ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8); @@ -212,7 +208,6 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press return 1; input_position--; - free(glyphs_ucs[input_position]); free(glyphs_utf8[input_position]); handle_expose(NULL, conn, NULL); @@ -243,18 +238,16 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press return 1; } - /* store the UCS into a string */ - uint8_t inp[3] = {(ucs & 0xFF00) >> 8, (ucs & 0xFF), 0}; + xcb_char2b_t inp; + inp.byte1 = ( ucs & 0xff00 ) >> 2; + inp.byte2 = ( ucs & 0x00ff ) >> 0; - printf("inp[0] = %02x, inp[1] = %02x, inp[2] = %02x\n", inp[0], inp[1], inp[2]); + printf("inp.byte1 = %02x, inp.byte2 = %02x\n", inp.byte1, inp.byte2); /* convert it to UTF-8 */ - char *out = convert_ucs2_to_utf8((xcb_char2b_t*)inp, 1); + char *out = convert_ucs2_to_utf8(&inp, 1); printf("converted to %s\n", out); - glyphs_ucs[input_position] = malloc(3 * sizeof(uint8_t)); - if (glyphs_ucs[input_position] == NULL) - err(EXIT_FAILURE, "malloc() failed\n"); - memcpy(glyphs_ucs[input_position], inp, 3); + glyphs_ucs[input_position] = inp; glyphs_utf8[input_position] = out; input_position++; From 53365fa887e3038636bc21add1ab9cc1bc1c2e1a Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 20:28:39 +0200 Subject: [PATCH 120/200] libi3: Rework draw_text We now have two versions of draw_text draw_text: Now takes an i3String draw_text_ascii: Designed for static strings in plain ASCII --- i3-config-wizard/main.c | 2 +- i3-input/main.c | 8 +++-- i3-nagbar/main.c | 6 ++-- i3bar/src/xcb.c | 6 ++-- include/libi3.h | 14 +++++--- libi3/font.c | 72 +++++++++++++++++++++++------------------ src/sighandler.c | 2 +- src/x.c | 4 +-- 8 files changed, 65 insertions(+), 49 deletions(-) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index be042673..4c04a6d2 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -128,7 +128,7 @@ static int handle_expose() { set_font(&font); #define txt(x, row, text) \ - draw_text(text, strlen(text), false, pixmap, pixmap_gc,\ + draw_text_ascii(text, pixmap, pixmap_gc,\ x, (row - 1) * font.height + 4, 300 - x * 2) if (current_step == STEP_WELCOME) { diff --git a/i3-input/main.c b/i3-input/main.c index 9ab40aec..6f5ad78a 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -98,11 +98,15 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t /* draw the prompt … */ if (prompt != NULL) { - draw_text((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true, pixmap, pixmap_gc, 4, 4, 492); + draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492); } /* … and the text */ if (input_position > 0) - draw_text((char *)glyphs_ucs, input_position, true, pixmap, pixmap_gc, 4, 4 + prompt_offset, 492 - prompt_offset); + { + i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); + draw_text(input, pixmap, pixmap_gc, 4, 4, 492); + i3string_free(input); + } /* Copy the contents of the pixmap to the real window */ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8); diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 7a09b99d..26a282f6 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -132,7 +132,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { /* restore font color */ set_font_colors(pixmap_gc, color_text, color_background); - draw_text((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true, pixmap, pixmap_gc, + draw_text(prompt, pixmap, pixmap_gc, 4 + 4, 4 + 4, rect.width - 4 - 4); /* render close button */ @@ -159,7 +159,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { values[0] = 1; set_font_colors(pixmap_gc, color_text, color_button_background); - draw_text("X", 1, false, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4, + draw_text_ascii("X", pixmap, pixmap_gc, y - w - line_width + w / 2 - 4, 4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4); y -= w; @@ -190,7 +190,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { values[0] = color_text; values[1] = color_button_background; set_font_colors(pixmap_gc, color_text, color_button_background); - draw_text((char *)i3string_as_ucs2(buttons[c].label), i3string_get_num_glyphs(buttons[c].label), true, pixmap, pixmap_gc, + draw_text(buttons[c].label, pixmap, pixmap_gc, y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6); y -= w; diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index be3ca35a..729803e1 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -144,8 +144,7 @@ void refresh_statusline() { uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); - draw_text((char *)i3string_as_ucs2(block->full_text), i3string_get_num_glyphs(block->full_text), - true, statusline_pm, statusline_ctx, x, 0, block->width); + draw_text(block->full_text, statusline_pm, statusline_ctx, x, 0, block->width); x += block->width; if (TAILQ_NEXT(block, blocks) != NULL) { @@ -1519,8 +1518,7 @@ void draw_bars() { 1, &rect); set_font_colors(outputs_walk->bargc, fg_color, bg_color); - draw_text((char*)i3string_as_ucs2(ws_walk->name), i3string_get_num_glyphs(ws_walk->name), true, - outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width); + draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width); i += 10 + ws_walk->name_width + 1; } diff --git a/include/libi3.h b/include/libi3.h index 97383ba3..29b8c107 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -297,13 +297,17 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background * specified coordinates (from the top left corner of the leftmost, uppermost * glyph) and using the provided gc. * - * Text can be specified as UCS-2 or UTF-8. If it's specified as UCS-2, then - * text_len must be the number of glyphs in the string. If it's specified as - * UTF-8, then text_len must be the number of bytes in the string (not counting - * the null terminator). + * Text must be specified as an i3String. * */ -void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable, +void draw_text(i3String *text, xcb_drawable_t drawable, + xcb_gcontext_t gc, int x, int y, int max_width); + +/** + * ASCII version of draw_text to print static strings. + * + */ +void draw_text_ascii(const char *text, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width); /** diff --git a/libi3/font.c b/libi3/font.c index 0b276b0b..853e5f92 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -105,45 +105,21 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background xcb_change_gc(conn, gc, mask, values); } -/* - * Draws text onto the specified X drawable (normally a pixmap) at the - * specified coordinates (from the top left corner of the leftmost, uppermost - * glyph) and using the provided gc. - * - * Text can be specified as UCS-2 or UTF-8. If it's specified as UCS-2, then - * text_len must be the number of glyphs in the string. If it's specified as - * UTF-8, then text_len must be the number of bytes in the string (not counting - * the null terminator). - * - */ -void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable, +static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width) { - assert(savedFont != NULL); - assert(text_len != 0); - /* X11 coordinates for fonts start at the baseline */ int pos_y = y + savedFont->info->font_ascent; - /* As an optimization, check if we can bypass conversion */ - if (!is_ucs2 && text_len <= 255) { - xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text); - return; - } - - /* Convert the text into UCS-2 so we can do basic pointer math */ - char *input = (is_ucs2 ? text : (char*)convert_utf8_to_ucs2(text, &text_len)); - /* The X11 protocol limits text drawing to 255 chars, so we may need * multiple calls */ - int pos_x = x; int offset = 0; for (;;) { /* Calculate the size of this chunk */ int chunk_size = (text_len > 255 ? 255 : text_len); - xcb_char2b_t *chunk = (xcb_char2b_t*)input + offset; + const xcb_char2b_t *chunk = text + offset; /* Draw it */ - xcb_image_text_16(conn, chunk_size, drawable, gc, pos_x, pos_y, chunk); + xcb_image_text_16(conn, chunk_size, drawable, gc, x, pos_y, chunk); /* Advance the offset and length of the text to draw */ offset += chunk_size; @@ -154,12 +130,46 @@ void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawabl break; /* Advance pos_x based on the predicted text width */ - pos_x += predict_text_width((char*)chunk, chunk_size, true); + x += predict_text_width((char*)chunk, chunk_size, true); } +} - /* If we had to convert, free the converted string */ - if (!is_ucs2) - free(input); +/* + * Draws text onto the specified X drawable (normally a pixmap) at the + * specified coordinates (from the top left corner of the leftmost, uppermost + * glyph) and using the provided gc. + * + * Text must be specified as an i3String. + * + */ +void draw_text(i3String *text, xcb_drawable_t drawable, + xcb_gcontext_t gc, int x, int y, int max_width) { + assert(savedFont != NULL); + + draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), + drawable, gc, x, y, max_width); +} + +/* + * ASCII version of draw_text to print static strings. + * + */ +void draw_text_ascii(const char *text, xcb_drawable_t drawable, + xcb_gcontext_t gc, int x, int y, int max_width) { + assert(savedFont != NULL); + + size_t text_len = strlen(text); + if (text_len > 255) { + /* The text is too long to draw it directly to X */ + i3String *str = i3string_from_utf8(text); + draw_text(str, drawable, gc, x, y, max_width); + i3string_free(str); + } else { + /* X11 coordinates for fonts start at the baseline */ + int pos_y = y + savedFont->info->font_ascent; + + xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text); + } } static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) { diff --git a/src/sighandler.c b/src/sighandler.c index 2c53333d..e9a4e9bf 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -52,7 +52,7 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); for (int i = 0; crash_text_i3strings[i] != NULL; ++i) { - draw_text((char *)i3string_as_ucs2(crash_text_i3strings[i]), i3string_get_num_glyphs(crash_text_i3strings[i]), true, pixmap, pixmap_gc, + draw_text(crash_text_i3strings[i], pixmap, pixmap_gc, 8, 5 + i * font_height, width - 16); } diff --git a/src/x.c b/src/x.c index 864949e5..24fd0eac 100644 --- a/src/x.c +++ b/src/x.c @@ -484,7 +484,7 @@ void x_draw_decoration(Con *con) { if (win == NULL || win->name == NULL) { /* this is a non-leaf container, we need to make up a good description */ // TODO: use a good description instead of just "another container" - draw_text("another container", strlen("another container"), false, + draw_text_ascii("another container", parent->pixmap, parent->pm_gc, con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, con->deco_rect.width - 2); @@ -508,7 +508,7 @@ void x_draw_decoration(Con *con) { //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult); int indent_px = (indent_level * 5) * indent_mult; - draw_text((char *)i3string_as_ucs2(win->name), i3string_get_num_glyphs(win->name), true, + draw_text(win->name, parent->pixmap, parent->pm_gc, con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, con->deco_rect.width - 2 - indent_px); From 210fc6dfed0917f44e3fe4d5fb56c9ede0edc03f Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 7 Aug 2012 22:30:37 +0200 Subject: [PATCH 121/200] libi3: Rework predict_text_width predict_text_width now takes an i3String as argument --- i3-input/main.c | 2 +- i3bar/src/workspaces.c | 5 ++--- i3bar/src/xcb.c | 2 +- include/libi3.h | 6 +++--- libi3/font.c | 34 ++++++++++++++++------------------ src/sighandler.c | 2 +- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index 6f5ad78a..2de5a41e 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -333,7 +333,7 @@ int main(int argc, char *argv[]) { sockfd = ipc_connect(socket_path); if (prompt != NULL) - prompt_offset = predict_text_width((char *)i3string_as_ucs2(prompt), i3string_get_num_glyphs(prompt), true); + prompt_offset = predict_text_width(prompt); int screens; conn = xcb_connect(NULL, &screens); diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index c77103f3..6db37983 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -116,10 +116,9 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne /* Save the name */ params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len); - /* Convert the name to ucs2, save its length in glyphs and calculate its rendered width */ + /* Save its rendered width */ params->workspaces_walk->name_width = - predict_text_width((char *)i3string_as_ucs2(params->workspaces_walk->name), - i3string_get_num_glyphs(params->workspaces_walk->name), true); + predict_text_width(params->workspaces_walk->name); DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n", i3string_as_utf8(params->workspaces_walk->name), diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 729803e1..06b3fb9a 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -119,7 +119,7 @@ void refresh_statusline() { if (i3string_get_num_bytes(block->full_text) == 0) continue; - block->width = predict_text_width((char *)i3string_as_ucs2(block->full_text), i3string_get_num_glyphs(block->full_text), true); + block->width = predict_text_width(block->full_text); /* If this is not the last block, add some pixels for a separator. */ if (TAILQ_NEXT(block, blocks) != NULL) block->width += 9; diff --git a/include/libi3.h b/include/libi3.h index 29b8c107..01b9992b 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -311,11 +311,11 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width); /** - * Predict the text width in pixels for the given text. Text can be specified - * as UCS-2 or UTF-8. + * Predict the text width in pixels for the given text. Text must be + * specified as an i3String. * */ -int predict_text_width(char *text, size_t text_len, bool is_ucs2); +int predict_text_width(i3String *text); /** * Returns true if this version of i3 is a debug build (anything which is not a diff --git a/libi3/font.c b/libi3/font.c index 853e5f92..823888f2 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -105,6 +105,8 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background xcb_change_gc(conn, gc, mask, values); } +static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len); + static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width) { /* X11 coordinates for fonts start at the baseline */ @@ -130,7 +132,7 @@ static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawabl break; /* Advance pos_x based on the predicted text width */ - x += predict_text_width((char*)chunk, chunk_size, true); + x += predict_text_width_xcb(chunk, chunk_size); } } @@ -172,7 +174,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, } } -static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) { +static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) { /* Make the user know we’re using the slow path, but only once. */ static bool first_invocation = true; if (first_invocation) { @@ -199,18 +201,9 @@ static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) { return width; } -/* - * Predict the text width in pixels for the given text. Text can be specified - * as UCS-2 or UTF-8. - * - */ -int predict_text_width(char *text, size_t text_len, bool is_ucs2) { - /* Convert the text into UTF-16 so we can do basic pointer math */ - xcb_char2b_t *input; - if (is_ucs2) - input = (xcb_char2b_t*)text; - else - input = convert_utf8_to_ucs2(text, &text_len); +static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) { + if (text_len == 0) + return 0; int width; if (savedFont->table == NULL) { @@ -249,9 +242,14 @@ int predict_text_width(char *text, size_t text_len, bool is_ucs2) { } } - /* If we had to convert, free the converted string */ - if (!is_ucs2) - free(input); - return width; } + +/* + * Predict the text width in pixels for the given text. Text must be + * specified as an i3String. + * + */ +int predict_text_width(i3String *text) { + return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); +} diff --git a/src/sighandler.c b/src/sighandler.c index e9a4e9bf..3a9307e1 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -155,7 +155,7 @@ void handle_signal(int sig, siginfo_t *info, void *data) { } crash_text_i3strings[crash_text_length] = NULL; /* calculate width for longest text */ - int font_width = predict_text_width((char *)i3string_as_ucs2(crash_text_i3strings[crash_text_longest]), i3string_get_num_glyphs(crash_text_i3strings[crash_text_longest]), true); + int font_width = predict_text_width(crash_text_i3strings[crash_text_longest]); int width = font_width + 20; /* Open a popup window on each virtual screen */ From 5d8ccc5912c4e5bbe9ac6ea6763503e7ecf758b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Mon, 14 Nov 2011 21:39:03 -0200 Subject: [PATCH 122/200] libi3: Introduce get_visualtype --- include/libi3.h | 6 ++++++ libi3/get_visualtype.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 libi3/get_visualtype.c diff --git a/include/libi3.h b/include/libi3.h index 01b9992b..eb573eb0 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -317,6 +317,12 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, */ int predict_text_width(i3String *text); +/** + * Returns the visual type associated with the given screen. + * + */ +xcb_visualtype_t *get_visualtype(xcb_screen_t *screen); + /** * Returns true if this version of i3 is a debug build (anything which is not a * release version), based on the git version number. diff --git a/libi3/get_visualtype.c b/libi3/get_visualtype.c new file mode 100644 index 00000000..d11722f0 --- /dev/null +++ b/libi3/get_visualtype.c @@ -0,0 +1,28 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * + */ +#include "libi3.h" + +/* + * Returns the visual type associated with the given screen. + * + */ +xcb_visualtype_t *get_visualtype(xcb_screen_t *screen) { + xcb_depth_iterator_t depth_iter; + for (depth_iter = xcb_screen_allowed_depths_iterator(screen); + depth_iter.rem; + xcb_depth_next(&depth_iter)) { + xcb_visualtype_iterator_t visual_iter; + for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data); + visual_iter.rem; + xcb_visualtype_next(&visual_iter)) { + if (screen->root_visual == visual_iter.data->visual_id) + return visual_iter.data; + } + } + return NULL; +} From edd9007ebf18120cbaeb9d0555f9a496a14617a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Mon, 14 Nov 2011 21:39:03 -0200 Subject: [PATCH 123/200] i3bar: Rename xcb_screen to root_screen for consistency --- i3bar/src/xcb.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 06b3fb9a..1d6a4d2b 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -47,7 +47,7 @@ xcb_atom_t atoms[NUM_ATOMS]; /* Variables, that are the same for all functions at all times */ xcb_connection_t *xcb_connection; int screen; -xcb_screen_t *xcb_screen; +xcb_screen_t *root_screen; xcb_window_t xcb_root; /* This is needed for integration with libi3 */ @@ -128,12 +128,12 @@ void refresh_statusline() { /* If the statusline is bigger than our screen we need to make sure that * the pixmap provides enough space, so re-allocate if the width grew */ - if (statusline_width > xcb_screen->width_in_pixels && + if (statusline_width > root_screen->width_in_pixels && statusline_width > old_statusline_width) realloc_sl_buffer(); /* Clear the statusline pixmap. */ - xcb_rectangle_t rect = { 0, 0, xcb_screen->width_in_pixels, font.height }; + xcb_rectangle_t rect = { 0, 0, root_screen->width_in_pixels, font.height }; xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect); /* Draw the text of each block. */ @@ -824,8 +824,8 @@ char *init_xcb_early() { #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name); #include "xcb_atoms.def" - xcb_screen = xcb_aux_get_screen(xcb_connection, screen); - xcb_root = xcb_screen->root; + root_screen = xcb_aux_get_screen(xcb_connection, screen); + xcb_root = root_screen->root; /* We draw the statusline to a seperate pixmap, because it looks the same on all bars and * this way, we can choose to crop it */ @@ -848,11 +848,11 @@ char *init_xcb_early() { statusline_pm = xcb_generate_id(xcb_connection); xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, statusline_pm, xcb_root, - xcb_screen->width_in_pixels, - xcb_screen->height_in_pixels); + root_screen->width_in_pixels, + root_screen->height_in_pixels); /* The various Watchers to communicate with xcb */ @@ -980,14 +980,14 @@ void init_tray() { uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT; uint32_t selval[] = { 1 }; xcb_create_window(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, selwin, xcb_root, -1, -1, 1, 1, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, - xcb_screen->root_visual, + root_screen->root_visual, selmask, selval); @@ -1146,16 +1146,16 @@ void destroy_window(i3_output *output) { * */ void realloc_sl_buffer() { - DLOG("Re-allocating statusline-buffer, statusline_width = %d, xcb_screen->width_in_pixels = %d\n", - statusline_width, xcb_screen->width_in_pixels); + DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n", + statusline_width, root_screen->width_in_pixels); xcb_free_pixmap(xcb_connection, statusline_pm); statusline_pm = xcb_generate_id(xcb_connection); xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, statusline_pm, xcb_root, - MAX(xcb_screen->width_in_pixels, statusline_width), - xcb_screen->height_in_pixels); + MAX(root_screen->width_in_pixels, statusline_width), + root_screen->height_in_pixels); uint32_t mask = XCB_GC_FOREGROUND; uint32_t vals[2] = { colors.bar_bg, colors.bar_bg }; @@ -1225,20 +1225,20 @@ void reconfig_windows() { values[2] |= XCB_EVENT_MASK_BUTTON_PRESS; } xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, walk->bar, xcb_root, walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6, walk->rect.w, font.height + 6, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, - xcb_screen->root_visual, + root_screen->root_visual, mask, values); /* The double-buffer we use to render stuff off-screen */ xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, walk->buffer, walk->bar, walk->rect.w, @@ -1378,7 +1378,7 @@ void reconfig_windows() { DLOG("Recreating buffer for output %s", walk->name); xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, - xcb_screen->root_depth, + root_screen->root_depth, walk->buffer, walk->bar, walk->rect.w, From ec17a26b0e858ca888319da6c7cdddd634a599eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Mon, 14 Nov 2011 21:39:03 -0200 Subject: [PATCH 124/200] libi3: Rework font to support multiple backends --- include/libi3.h | 27 +++++++--- libi3/font.c | 137 ++++++++++++++++++++++++++++++++++-------------- src/config.c | 2 +- src/xcb.c | 10 ++-- 4 files changed, 125 insertions(+), 51 deletions(-) diff --git a/include/libi3.h b/include/libi3.h index eb573eb0..2c468156 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -33,17 +33,28 @@ typedef struct Font i3Font; * */ struct Font { - /** The xcb-id for the font */ - xcb_font_t id; - - /** Font information gathered from the server */ - xcb_query_font_reply_t *info; - - /** Font table for this font (may be NULL) */ - xcb_charinfo_t *table; + /** The type of font */ + enum { + FONT_TYPE_NONE = 0, + FONT_TYPE_XCB, + FONT_TYPE_PANGO + } type; /** The height of the font, built from font_ascent + font_descent */ int height; + + union { + struct { + /** The xcb-id for the font */ + xcb_font_t id; + + /** Font information gathered from the server */ + xcb_query_font_reply_t *info; + + /** Font table for this font (may be NULL) */ + xcb_charinfo_t *table; + } xcb; + } specific; }; /* Since this file also gets included by utilities which don’t use the i3 log diff --git a/libi3/font.c b/libi3/font.c index 823888f2..7f8c7bb1 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -24,12 +24,14 @@ static const i3Font *savedFont = NULL; */ i3Font load_font(const char *pattern, const bool fallback) { i3Font font; + font.type = FONT_TYPE_NONE; + /* Send all our requests first */ - font.id = xcb_generate_id(conn); - xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.id, + font.specific.xcb.id = xcb_generate_id(conn); + xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id, strlen(pattern), pattern); - xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.id); + xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.specific.xcb.id); /* Check for errors. If errors, fall back to default font. */ xcb_generic_error_t *error; @@ -40,8 +42,9 @@ i3Font load_font(const char *pattern, const bool fallback) { ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n", pattern, error->error_code); pattern = "fixed"; - font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern); - info_cookie = xcb_query_font(conn, font.id); + font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id, + strlen(pattern), pattern); + info_cookie = xcb_query_font(conn, font.specific.xcb.id); /* Check if we managed to open 'fixed' */ error = xcb_request_check(conn, font_cookie); @@ -50,8 +53,9 @@ i3Font load_font(const char *pattern, const bool fallback) { if (error != NULL) { ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n"); pattern = "-misc-*"; - font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern); - info_cookie = xcb_query_font(conn, font.id); + font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id, + strlen(pattern), pattern); + info_cookie = xcb_query_font(conn, font.specific.xcb.id); if ((error = xcb_request_check(conn, font_cookie)) != NULL) errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks " @@ -60,18 +64,20 @@ i3Font load_font(const char *pattern, const bool fallback) { } /* Get information (height/name) for this font */ - if (!(font.info = xcb_query_font_reply(conn, info_cookie, NULL))) + if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL))) errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern); /* Get the font table, if possible */ - if (xcb_query_font_char_infos_length(font.info) == 0) - font.table = NULL; + if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0) + font.specific.xcb.table = NULL; else - font.table = xcb_query_font_char_infos(font.info); + font.specific.xcb.table = xcb_query_font_char_infos(font.specific.xcb.info); /* Calculate the font height */ - font.height = font.info->font_ascent + font.info->font_descent; + font.height = font.specific.xcb.info->font_ascent + font.specific.xcb.info->font_descent; + /* Set the font type and return successfully */ + font.type = FONT_TYPE_XCB; return font; } @@ -88,10 +94,21 @@ void set_font(i3Font *font) { * */ void free_font(void) { - /* Close the font and free the info */ - xcb_close_font(conn, savedFont->id); - if (savedFont->info) - free(savedFont->info); + switch (savedFont->type) { + case FONT_TYPE_NONE: + /* Nothing to do */ + break; + case FONT_TYPE_XCB: { + /* Close the font and free the info */ + xcb_close_font(conn, savedFont->specific.xcb.id); + if (savedFont->specific.xcb.info) + free(savedFont->specific.xcb.info); + break; + } + default: + assert(false); + break; + } } /* @@ -100,9 +117,22 @@ void free_font(void) { */ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background) { assert(savedFont != NULL); - uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; - uint32_t values[] = { foreground, background, savedFont->id }; - xcb_change_gc(conn, gc, mask, values); + + switch (savedFont->type) { + case FONT_TYPE_NONE: + /* Nothing to do */ + break; + case FONT_TYPE_XCB: { + /* Change the font and colors in the GC */ + uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; + uint32_t values[] = { foreground, background, savedFont->specific.xcb.id }; + xcb_change_gc(conn, gc, mask, values); + break; + } + default: + assert(false); + break; + } } static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len); @@ -110,7 +140,7 @@ static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len); static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width) { /* X11 coordinates for fonts start at the baseline */ - int pos_y = y + savedFont->info->font_ascent; + int pos_y = y + savedFont->specific.xcb.info->font_ascent; /* The X11 protocol limits text drawing to 255 chars, so we may need * multiple calls */ @@ -148,8 +178,17 @@ void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width) { assert(savedFont != NULL); - draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), - drawable, gc, x, y, max_width); + switch (savedFont->type) { + case FONT_TYPE_NONE: + /* Nothing to do */ + return; + case FONT_TYPE_XCB: + draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), + drawable, gc, x, y, max_width); + break; + default: + assert(false); + } } /* @@ -160,17 +199,28 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, xcb_gcontext_t gc, int x, int y, int max_width) { assert(savedFont != NULL); - size_t text_len = strlen(text); - if (text_len > 255) { - /* The text is too long to draw it directly to X */ - i3String *str = i3string_from_utf8(text); - draw_text(str, drawable, gc, x, y, max_width); - i3string_free(str); - } else { - /* X11 coordinates for fonts start at the baseline */ - int pos_y = y + savedFont->info->font_ascent; + switch (savedFont->type) { + case FONT_TYPE_NONE: + /* Nothing to do */ + return; + case FONT_TYPE_XCB: + { + size_t text_len = strlen(text); + if (text_len > 255) { + /* The text is too long to draw it directly to X */ + i3String *str = i3string_from_utf8(text); + draw_text(str, drawable, gc, x, y, max_width); + i3string_free(str); + } else { + /* X11 coordinates for fonts start at the baseline */ + int pos_y = y + savedFont->specific.xcb.info->font_ascent; - xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text); + xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text); + } + break; + } + default: + assert(false); } } @@ -185,7 +235,7 @@ static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) { /* Query the text width */ xcb_generic_error_t *error; xcb_query_text_extents_cookie_t cookie = xcb_query_text_extents(conn, - savedFont->id, text_len, (xcb_char2b_t*)text); + savedFont->specific.xcb.id, text_len, (xcb_char2b_t*)text); xcb_query_text_extents_reply_t *reply = xcb_query_text_extents_reply(conn, cookie, &error); if (reply == NULL) { @@ -193,7 +243,7 @@ static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) { * a crash. Plus, the user will see the error in his log. */ fprintf(stderr, "Could not get text extents (X error code %d)\n", error->error_code); - return savedFont->info->max_bounds.character_width * text_len; + return savedFont->specific.xcb.info->max_bounds.character_width * text_len; } int width = reply->overall_width; @@ -206,13 +256,13 @@ static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) { return 0; int width; - if (savedFont->table == NULL) { + if (savedFont->specific.xcb.table == NULL) { /* If we don't have a font table, fall back to querying the server */ width = xcb_query_text_width(input, text_len); } else { /* Save some pointers for convenience */ - xcb_query_font_reply_t *font_info = savedFont->info; - xcb_charinfo_t *font_table = savedFont->table; + xcb_query_font_reply_t *font_info = savedFont->specific.xcb.info; + xcb_charinfo_t *font_table = savedFont->specific.xcb.table; /* Calculate the width using the font table */ width = 0; @@ -251,5 +301,16 @@ static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) { * */ int predict_text_width(i3String *text) { - return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); + assert(savedFont != NULL); + + switch (savedFont->type) { + case FONT_TYPE_NONE: + /* Nothing to do */ + return 0; + case FONT_TYPE_XCB: + return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); + default: + assert(false); + return 0; + } } diff --git a/src/config.c b/src/config.c index c256a3b7..c5846279 100644 --- a/src/config.c +++ b/src/config.c @@ -394,7 +394,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, grab_all_keys(conn, false); } - if (config.font.id == 0) { + if (config.font.type == FONT_TYPE_NONE) { ELOG("You did not specify required configuration option \"font\"\n"); config.font = load_font("fixed", true); set_font(&config.font); diff --git a/src/xcb.c b/src/xcb.c index be3f536a..caa203f7 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -50,8 +50,9 @@ xcb_window_t create_window(xcb_connection_t *conn, Rect dims, xcb_cursor_t cursor_id = xcb_generate_id(conn); i3Font cursor_font = load_font("cursor", false); int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.id, cursor_font.id, - xcb_cursor, xcb_cursor + 1, 0, 0, 0, 65535, 65535, 65535); + xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, + cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, + 65535, 65535, 65535); xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id); xcb_free_cursor(conn, cursor_id); } @@ -195,8 +196,9 @@ void xcb_set_root_cursor(int cursor) { xcb_cursor_t cursor_id = xcb_generate_id(conn); i3Font cursor_font = load_font("cursor", false); int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.id, cursor_font.id, - xcb_cursor, xcb_cursor + 1, 0, 0, 0, 65535, 65535, 65535); + xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, + cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, + 65535, 65535, 65535); xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id); xcb_free_cursor(conn, cursor_id); xcb_flush(conn); From f06161b58a20dc2c3cdeca849dd3cd4dca20e812 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 5 Aug 2012 20:54:30 +0200 Subject: [PATCH 125/200] common.mk: Check for Pango --- common.mk | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common.mk b/common.mk index a6d6c4e3..989ed5ea 100644 --- a/common.mk +++ b/common.mk @@ -135,6 +135,13 @@ PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre) LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0) LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) +# Pango +PANGO_CFLAGS := $(call cflags_for_lib, cairo) +PANGO_CFLAGS += $(call cflags_for_lib, pangocairo) +I3_CPPFLAGS += -DPANGO_SUPPORT=1 +PANGO_LIBS := $(call ldflags_for_lib, cairo) +PANGO_LIBS += $(call ldflags_for_lib, pangocairo) + # libi3 LIBS = -L$(TOPDIR) -li3 From 6ff3f7abad8bed8c82302a671091d2ad3ac0fa6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sun, 5 Aug 2012 21:36:49 +0200 Subject: [PATCH 126/200] libi3: Implement Pango rendering --- i3-config-wizard/i3-config-wizard.mk | 4 +- i3-config-wizard/main.c | 3 +- i3-dump-log/i3-dump-log.mk | 2 +- i3-input/i3-input.mk | 4 +- i3-input/main.c | 3 +- i3-msg/i3-msg.mk | 2 +- i3-nagbar/i3-nagbar.mk | 4 +- i3-nagbar/main.c | 3 +- i3bar/i3bar.mk | 4 +- i3bar/include/util.h | 2 + include/libi3.h | 9 ++ libi3/font.c | 148 +++++++++++++++++++++++++++ libi3/libi3.mk | 2 +- src/i3.mk | 4 +- 14 files changed, 178 insertions(+), 16 deletions(-) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 526a4ecd..7e9c4bee 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) -i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) +i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) +i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index 4c04a6d2..46cf8aa8 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -69,6 +69,7 @@ enum { MOD_Mod1, MOD_Mod4 } modifier = MOD_Mod4; static char *config_path; static uint32_t xcb_numlock_mask; xcb_connection_t *conn; +xcb_screen_t *root_screen; static xcb_get_modifier_mapping_reply_t *modmap_reply; static i3Font font; static i3Font bold_font; @@ -456,7 +457,7 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index 17e53a6d..e696546d 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -4,7 +4,7 @@ CLEAN_TARGETS += clean-i3-dump-log i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) -i3_dump_log_CFLAGS = $(XCB_CFLAGS) +i3_dump_log_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) i3_dump_log_LIBS = $(XCB_LIBS) i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 98131e76..d31a11ff 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-input i3_input_SOURCES := $(wildcard i3-input/*.c) i3_input_HEADERS := $(wildcard i3-input/*.h) -i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) -i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) +i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) +i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) diff --git a/i3-input/main.c b/i3-input/main.c index 2de5a41e..d18502cd 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -54,6 +54,7 @@ static int prompt_offset = 0; static int limit; xcb_window_t root; xcb_connection_t *conn; +xcb_screen_t *root_screen; /* * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for @@ -340,7 +341,7 @@ int main(int argc, char *argv[]) { if (!conn || xcb_connection_has_error(conn)) die("Cannot open display\n"); - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; symbols = xcb_key_symbols_alloc(conn); diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index 01d5fc7e..c3a59303 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -4,7 +4,7 @@ CLEAN_TARGETS += clean-i3-msg i3_msg_SOURCES := $(wildcard i3-msg/*.c) i3_msg_HEADERS := $(wildcard i3-msg/*.h) -i3_msg_CFLAGS = $(XCB_CFLAGS) +i3_msg_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) i3_msg_LIBS = $(XCB_LIBS) i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 5e5f2c4d..4fea1629 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) -i3_nagbar_CFLAGS = $(XCB_CFLAGS) -i3_nagbar_LIBS = $(XCB_LIBS) +i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) +i3_nagbar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 26a282f6..1588c356 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -54,6 +54,7 @@ static uint32_t color_text; /* color of the text */ xcb_window_t root; xcb_connection_t *conn; +xcb_screen_t *root_screen; /* * Starts the given application by passing it through a shell. We use double fork @@ -280,7 +281,7 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; if (bar_type == TYPE_ERROR) { diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 5b4ddd80..c311adfe 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) +i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/i3bar/include/util.h b/i3bar/include/util.h index 262d7750..43c56c58 100644 --- a/i3bar/include/util.h +++ b/i3bar/include/util.h @@ -11,7 +11,9 @@ #include "queue.h" /* Get the maximum/minimum of x and y */ +#undef MAX #define MAX(x,y) ((x) > (y) ? (x) : (y)) +#undef MIN #define MIN(x,y) ((x) < (y) ? (x) : (y)) /* Securely free p */ diff --git a/include/libi3.h b/include/libi3.h index 2c468156..7b58b96f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -18,6 +18,10 @@ #include #include +#if PANGO_SUPPORT +#include +#endif + /** * Opaque data structure for storing strings. * @@ -54,6 +58,11 @@ struct Font { /** Font table for this font (may be NULL) */ xcb_charinfo_t *table; } xcb; + +#if PANGO_SUPPORT + /** The pango font description */ + PangoFontDescription *pango_desc; +#endif } specific; }; diff --git a/libi3/font.c b/libi3/font.c index 7f8c7bb1..d9683e54 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -12,11 +12,118 @@ #include #include +#if PANGO_SUPPORT +#include +#include +#endif + #include "libi3.h" extern xcb_connection_t *conn; +extern xcb_screen_t *root_screen; + static const i3Font *savedFont = NULL; +#if PANGO_SUPPORT +static xcb_visualtype_t *root_visual_type; +static double pango_font_red; +static double pango_font_green; +static double pango_font_blue; + +/* + * Loads a Pango font description into an i3Font structure. Returns true + * on success, false otherwise. + * + */ +static bool load_pango_font(i3Font *font, const char *desc) { + /* Load the font description */ + font->specific.pango_desc = pango_font_description_from_string(desc); + if (!font->specific.pango_desc) + return false; + + /* We cache root_visual_type here, since you must call + * load_pango_font before any other pango function + * that would need root_visual_type */ + root_visual_type = get_visualtype(root_screen); + + /* Create a dummy Pango layout to compute the font height */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description(layout, font->specific.pango_desc); + + /* Get the font height */ + gint height; + pango_layout_get_pixel_size(layout, NULL, &height); + font->height = height; + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + + /* Set the font type and return successfully */ + font->type = FONT_TYPE_PANGO; + return true; +} + +/* + * Draws text using Pango rendering. + * + */ +static void draw_text_pango(const char *text, size_t text_len, + xcb_drawable_t drawable, int x, int y, int max_width) { + /* Create the Pango layout */ + /* root_visual_type is cached in load_pango_font */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, + root_visual_type, x + max_width, y + savedFont->height); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description(layout, savedFont->specific.pango_desc); + pango_layout_set_width(layout, max_width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + + /* Do the drawing */ + cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); + cairo_move_to(cr, x, y); + pango_layout_set_text(layout, text, text_len); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +/* + * Calculate the text width using Pango rendering. + * + */ +static int predict_text_width_pango(const char *text, size_t text_len) { + /* Create a dummy Pango layout */ + /* root_visual_type is cached in load_pango_font */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + + /* Get the font width */ + gint width; + pango_layout_set_font_description(layout, savedFont->specific.pango_desc); + pango_layout_set_text(layout, text, text_len); + pango_cairo_update_layout(cr, layout); + pango_layout_get_pixel_size(layout, &width, NULL); + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + + return width; +} +#endif + /* * Loads a font for usage, also getting its metrics. If fallback is true, * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. @@ -26,6 +133,14 @@ i3Font load_font(const char *pattern, const bool fallback) { i3Font font; font.type = FONT_TYPE_NONE; +#if PANGO_SUPPORT + /* Try to load a pango font if specified */ + if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) { + pattern += strlen("xft:"); + if (load_pango_font(&font, pattern)) + return font; + } +#endif /* Send all our requests first */ font.specific.xcb.id = xcb_generate_id(conn); @@ -105,6 +220,12 @@ void free_font(void) { free(savedFont->specific.xcb.info); break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Free the font description */ + pango_font_description_free(savedFont->specific.pango_desc); + break; +#endif default: assert(false); break; @@ -129,6 +250,14 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background xcb_change_gc(conn, gc, mask, values); break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Save the foreground font */ + pango_font_red = ((foreground >> 16) & 0xff) / 255.0; + pango_font_green = ((foreground >> 8) & 0xff) / 255.0; + pango_font_blue = (foreground & 0xff) / 255.0; + break; +#endif default: assert(false); break; @@ -186,6 +315,13 @@ void draw_text(i3String *text, xcb_drawable_t drawable, draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), drawable, gc, x, y, max_width); break; +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Render the text using Pango */ + draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), + drawable, x, y, max_width); + return; +#endif default: assert(false); } @@ -219,6 +355,13 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, } break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Render the text using Pango */ + draw_text_pango(text, strlen(text), + drawable, x, y, max_width); + return; +#endif default: assert(false); } @@ -309,6 +452,11 @@ int predict_text_width(i3String *text) { return 0; case FONT_TYPE_XCB: return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Calculate extents using Pango */ + return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text)); +#endif default: assert(false); return 0; diff --git a/libi3/libi3.mk b/libi3/libi3.mk index 6f6bf506..b6a90995 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -2,7 +2,7 @@ CLEAN_TARGETS += clean-libi3 libi3_SOURCES := $(wildcard libi3/*.c) libi3_HEADERS := $(wildcard libi3/*.h) -libi3_CFLAGS = +libi3_CFLAGS = $(PANGO_CFLAGS) libi3_LIBS = libi3_OBJECTS := $(libi3_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 9591e178..bc9eabe8 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread +i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread # When using clang, we use pre-compiled headers to speed up the build. With # gcc, this actually makes the build slower. From 9f7247fd9c5478f2c24c8e6fc713200e2c7cbc9e Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 12 Aug 2012 12:20:15 +0200 Subject: [PATCH 127/200] libi3: Introduce LOG --- include/libi3.h | 5 ++++- include/log.h | 11 +++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/libi3.h b/include/libi3.h index 7b58b96f..45ba970f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -68,8 +68,11 @@ struct Font { /* Since this file also gets included by utilities which don’t use the i3 log * infrastructure, we define a fallback. */ +#if !defined(LOG) +#define LOG(fmt, ...) fprintf(stdout, "[libi3] " __FILE__ " " fmt, ##__VA_ARGS__) +#endif #if !defined(ELOG) -#define ELOG(fmt, ...) fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__) +#define ELOG(fmt, ...) fprintf(stderr, "[libi3] ERROR: " fmt, ##__VA_ARGS__) #endif /** diff --git a/include/log.h b/include/log.h index 26e85f04..7822fba5 100644 --- a/include/log.h +++ b/include/log.h @@ -13,14 +13,17 @@ #include #include -/** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that - is, delete the preceding comma */ -#define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) -/* We will include libi3.h which define its own version of ELOG. +/* We will include libi3.h which define its own version of LOG, ELOG. * We want *our* version, so we undef the libi3 one. */ +#if defined(LOG) +#undef LOG +#endif #if defined(ELOG) #undef ELOG #endif +/** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that + is, delete the preceding comma */ +#define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__) #define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) From 310f54293756bd06b7cb28ebfec4448956ee10b8 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 12 Aug 2012 12:20:51 +0200 Subject: [PATCH 128/200] libi3/font: Log the used font --- libi3/font.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libi3/font.c b/libi3/font.c index d9683e54..0688bdbc 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -39,7 +39,15 @@ static bool load_pango_font(i3Font *font, const char *desc) { /* Load the font description */ font->specific.pango_desc = pango_font_description_from_string(desc); if (!font->specific.pango_desc) + { + ELOG("Could not open font %s with Pango, fallback to X font.\n", desc); return false; + } + + LOG("Using Pango font %s, size %d", + pango_font_description_get_family(font->specific.pango_desc), + pango_font_description_get_size(font->specific.pango_desc) + ); /* We cache root_visual_type here, since you must call * load_pango_font before any other pango function @@ -178,6 +186,8 @@ i3Font load_font(const char *pattern, const bool fallback) { } } + LOG("Using X font %s", pattern); + /* Get information (height/name) for this font */ if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL))) errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern); From 6c9bf84d4eb4de680aee6de3cdaae553d10bfa79 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 12 Aug 2012 12:37:39 +0200 Subject: [PATCH 129/200] userguide: Update Fonts section for Pango support --- docs/userguide | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/userguide b/docs/userguide index 37e55d46..74e5fc59 100644 --- a/docs/userguide +++ b/docs/userguide @@ -294,11 +294,21 @@ a # and can only be used at the beginning of a line: # This is a comment ------------------- +[[fonts]] + === Fonts -i3 uses X core fonts (not Xft) for rendering window titles. You can use -+xfontsel(1)+ to generate such a font description. To see special characters -(Unicode), you need to use a font which supports the ISO-10646 encoding. +i3 has support for both X core fonts and FreeType fonts (through Pango) to +render window titles. + +To generate an X core font description, you can use +xfontsel(1)+. To see +special characters (Unicode), you need to use a font which supports the +ISO-10646 encoding. + +A FreeType font description is composed by a font family, a style, a weight, +a variant, a stretch and a size. +FreeType fonts support right-to-left rendering and contain often more +Unicode glyphs than X core fonts. If i3 cannot open the configured font, it will output an error in the logfile and fall back to a working font. @@ -306,11 +316,13 @@ and fall back to a working font. *Syntax*: ------------------------------ font +font xft: ------------------------------ *Examples*: -------------------------------------------------------------- font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +font xft:DejaVu Sans Mono 10 -------------------------------------------------------------- [[keybindings]] @@ -1029,8 +1041,7 @@ xrandr --output --primary === Font -Specifies the font (again, X core font, not Xft, just like in i3) to be used in -the bar. +Specifies the font to be used in the bar. See <>. *Syntax*: --------------------- @@ -1041,6 +1052,7 @@ font -------------------------------------------------------------- bar { font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 + font xft:DejaVu Sans Mono 10 } -------------------------------------------------------------- From 2896ae80574c237afb64427a6a5412994c87fa30 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:27:00 +0200 Subject: [PATCH 130/200] logging: make libi3 use verboselog()/errorlog(), provide it in each caller While this is a bit ugly, it makes the log messages end up where they are supposed to: in the shmlog/stdout in case of i3 and on stdout in case of utilities such as i3-input --- i3-config-wizard/main.c | 20 ++++++++++++++++++++ i3-input/main.c | 20 ++++++++++++++++++++ i3-nagbar/main.c | 20 ++++++++++++++++++++ i3bar/src/main.c | 22 +++++++++++++++++++++- include/libi3.h | 6 ++++-- libi3/font.c | 7 +++---- 6 files changed, 88 insertions(+), 7 deletions(-) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index 46cf8aa8..679c5e6d 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -84,6 +84,26 @@ Display *dpy; char *rewrite_binding(const char *bindingline); static void finish(); +/* + * Having verboselog() and errorlog() is necessary when using libi3. + * + */ +void verboselog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void errorlog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + /* * This function resolves ~ in pathnames. * It may resolve wildcards in the first part of the path, but if no match diff --git a/i3-input/main.c b/i3-input/main.c index d18502cd..3172387d 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -56,6 +56,26 @@ xcb_window_t root; xcb_connection_t *conn; xcb_screen_t *root_screen; +/* + * Having verboselog() and errorlog() is necessary when using libi3. + * + */ +void verboselog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void errorlog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + /* * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for * rendering it (UCS-2) or sending it to i3 (UTF-8). diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 1588c356..7aee191c 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -56,6 +56,26 @@ xcb_window_t root; xcb_connection_t *conn; xcb_screen_t *root_screen; +/* + * Having verboselog() and errorlog() is necessary when using libi3. + * + */ +void verboselog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void errorlog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + /* * Starts the given application by passing it through a shell. We use double fork * to avoid zombie processes. As the started application’s parent exits (immediately), diff --git a/i3bar/src/main.c b/i3bar/src/main.c index e648e00e..ea605647 100644 --- a/i3bar/src/main.c +++ b/i3bar/src/main.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * */ #include @@ -17,6 +17,26 @@ #include "common.h" +/* + * Having verboselog() and errorlog() is necessary when using libi3. + * + */ +void verboselog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void errorlog(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + /* * Glob path, i.e. expand ~ * diff --git a/include/libi3.h b/include/libi3.h index 45ba970f..d4df901f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -69,10 +69,12 @@ struct Font { /* Since this file also gets included by utilities which don’t use the i3 log * infrastructure, we define a fallback. */ #if !defined(LOG) -#define LOG(fmt, ...) fprintf(stdout, "[libi3] " __FILE__ " " fmt, ##__VA_ARGS__) +void verboselog(char *fmt, ...); +#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__) #endif #if !defined(ELOG) -#define ELOG(fmt, ...) fprintf(stderr, "[libi3] ERROR: " fmt, ##__VA_ARGS__) +void errorlog(char *fmt, ...); +#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__) #endif /** diff --git a/libi3/font.c b/libi3/font.c index 0688bdbc..08725844 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -38,13 +38,12 @@ static double pango_font_blue; static bool load_pango_font(i3Font *font, const char *desc) { /* Load the font description */ font->specific.pango_desc = pango_font_description_from_string(desc); - if (!font->specific.pango_desc) - { + if (!font->specific.pango_desc) { ELOG("Could not open font %s with Pango, fallback to X font.\n", desc); return false; } - LOG("Using Pango font %s, size %d", + LOG("Using Pango font %s, size %d\n", pango_font_description_get_family(font->specific.pango_desc), pango_font_description_get_size(font->specific.pango_desc) ); @@ -186,7 +185,7 @@ i3Font load_font(const char *pattern, const bool fallback) { } } - LOG("Using X font %s", pattern); + LOG("Using X font %s\n", pattern); /* Get information (height/name) for this font */ if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL))) From ef90ccd1a8e55460145f73f8d8e51507d352c28d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:34:30 +0200 Subject: [PATCH 131/200] default config: include a short explanation about xft fonts --- i3.config | 8 +++++++- i3.config.keycodes | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/i3.config b/i3.config index d29155ed..e45b31ba 100644 --- a/i3.config +++ b/i3.config @@ -9,8 +9,14 @@ # layout, use the i3-config-wizard # -# font for window titles. ISO 10646 = Unicode +# Font for window titles. Will also be used by the bar unless a different font +# is used in the bar {} block below. ISO 10646 = Unicode font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +# The font above is very space-efficient, that is, it looks good, sharp and +# clear in small sizes. However, if you need a lot of unicode glyphs or +# right-to-left text rendering, you should instead use pango for rendering and +# chose an xft font, such as: +# font xft:DejaVu Sans Mono 10 # use Mouse+Mod1 to drag floating windows to their wanted position floating_modifier Mod1 diff --git a/i3.config.keycodes b/i3.config.keycodes index 1bff8890..162660d3 100644 --- a/i3.config.keycodes +++ b/i3.config.keycodes @@ -10,8 +10,14 @@ set $mod Mod1 -# font for window titles. ISO 10646 = Unicode +# Font for window titles. Will also be used by the bar unless a different font +# is used in the bar {} block below. ISO 10646 = Unicode font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +# The font above is very space-efficient, that is, it looks good, sharp and +# clear in small sizes. However, if you need a lot of unicode glyphs or +# right-to-left text rendering, you should instead use pango for rendering and +# chose an xft font, such as: +# font xft:DejaVu Sans Mono 10 # Use Mouse+$mod to drag floating windows to their wanted position floating_modifier $mod From d7b11bde283855633bd1b3e695d88d024a0abcd8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:38:04 +0200 Subject: [PATCH 132/200] i3 --moreversion: use readlink /proc/$pid/exe instead of realpath(argv[0]) The latter is actually wrong. For example, when running i3 --moreversion, it will print $(pwd)/i3 instead of $(which i3). In my previous tests, this coincidentally was the same. --- src/display_version.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/display_version.c b/src/display_version.c index 7460a1a2..ac1a622c 100644 --- a/src/display_version.c +++ b/src/display_version.c @@ -123,18 +123,25 @@ void display_running_version(void) { errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply); printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom); - printf("\n"); - char resolved_path[PATH_MAX]; - if (realpath(start_argv[0], resolved_path) == NULL) - err(EXIT_FAILURE, "realpath(%s)", start_argv[0]); - printf("The i3 binary you just called: %s\n", resolved_path); #ifdef __linux__ char exepath[PATH_MAX], destpath[PATH_MAX]; + ssize_t linksize; + + snprintf(exepath, sizeof(exepath), "/proc/%d/exe", getpid()); + + if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1) + err(EXIT_FAILURE, "readlink(%s)", exepath); + + /* readlink() does not NULL-terminate strings, so we have to. */ + destpath[linksize] = '\0'; + + printf("\n"); + printf("The i3 binary you just called: %s\n", destpath); + snprintf(exepath, sizeof(exepath), "/proc/%s/exe", pid_from_atom); - ssize_t linksize; if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1) err(EXIT_FAILURE, "readlink(%s)", exepath); From 125bf09e70866f4e4c91fc4b48dee5ae9d0c374a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:39:58 +0200 Subject: [PATCH 133/200] pango: divide by PANGO_SCALE to get a "normal" font size --- libi3/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi3/font.c b/libi3/font.c index 08725844..23d7420d 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -45,7 +45,7 @@ static bool load_pango_font(i3Font *font, const char *desc) { LOG("Using Pango font %s, size %d\n", pango_font_description_get_family(font->specific.pango_desc), - pango_font_description_get_size(font->specific.pango_desc) + pango_font_description_get_size(font->specific.pango_desc) / PANGO_SCALE ); /* We cache root_visual_type here, since you must call From 1173740a358643a82d5b071551736546883e0c80 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:44:36 +0200 Subject: [PATCH 134/200] debian/control: format build-depends nicely, add pango/cairo --- debian/control | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 3a4adc68..a5c4c0fc 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,26 @@ Source: i3-wm Section: x11 Priority: extra Maintainer: Michael Stapelberg -Build-Depends: debhelper (>= 7.0.50~), libx11-dev, libxcb-util0-dev (>= 0.3.8), libxcb-keysyms1-dev, libxcb-xinerama0-dev (>= 1.1), libxcb-randr0-dev, libxcb-icccm4-dev, libxcursor-dev, asciidoc (>= 8.4.4), xmlto, docbook-xml, pkg-config, libev-dev, flex, bison, libyajl-dev, libpcre3-dev, libstartup-notification0-dev (>= 0.10) +Build-Depends: debhelper (>= 7.0.50~), + libx11-dev, + libxcb-util0-dev (>= 0.3.8), + libxcb-keysyms1-dev, + libxcb-xinerama0-dev (>= 1.1), + libxcb-randr0-dev, + libxcb-icccm4-dev, + libxcursor-dev, + asciidoc (>= 8.4.4), + xmlto, + docbook-xml, + pkg-config, + libev-dev, + flex, + bison, + libyajl-dev, + libpcre3-dev, + libstartup-notification0-dev (>= 0.10), + libcairo2-dev, + libpango1.0-dev Standards-Version: 3.9.3 Homepage: http://i3wm.org/ From ba8b5c480da0e007c523db6aa5b668b2836f0d57 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 13 Aug 2012 13:44:54 +0200 Subject: [PATCH 135/200] debian: make 'i3' recommend dunst --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index a5c4c0fc..4ec5cbc8 100644 --- a/debian/control +++ b/debian/control @@ -28,7 +28,7 @@ Homepage: http://i3wm.org/ Package: i3 Architecture: any Depends: i3-wm (=${binary:Version}), ${misc:Depends} -Recommends: i3lock (>= 2.2), suckless-tools, i3status (>= 2.3) +Recommends: i3lock (>= 2.2), suckless-tools, i3status (>= 2.3), dunst Description: metapackage (i3 window manager, screen locker, menu, statusbar) This metapackage installs the i3 window manager (i3-wm), the i3lock screen locker, i3status (for system information) and suckless-tools (for dmenu). From 5257a1268f3b0f0f6ca06f9638b362f311ce3fcb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 17 Aug 2012 01:53:45 +0200 Subject: [PATCH 136/200] =?UTF-8?q?Bugfix:=20don=E2=80=99t=20change=20tabb?= =?UTF-8?q?ed/stacked=20cons=20to=20splitv/splith=20layout=20(Thanks=20Mer?= =?UTF-8?q?ovius)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To automagically do the right thing when rotating monitors with regards to splith/splitv layout (depending on width/height of the monitor), we change the orientation of existing workspaces and the first child. If that first child happens to be a stacked/tabbed con, we cannot change the layout unconditionally (previously, the orientation was not in the layout, so we never noticed this problem). fixes #768 --- src/randr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/randr.c b/src/randr.c index 6971ba97..8b6ba1d9 100644 --- a/src/randr.c +++ b/src/randr.c @@ -464,7 +464,8 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) { workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH; DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout); if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) { - child->layout = workspace->layout; + if (child->layout == L_SPLITV || child->layout == L_SPLITH) + child->layout = workspace->layout; DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout); } } From ad21037cd2822b7154db1beb13fdcec123f6648b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 18 Aug 2012 15:33:36 +0200 Subject: [PATCH 137/200] update DEPENDS, prepare RELEASE-NOTES for 4.3 --- DEPENDS | 2 + RELEASE-NOTES-4.3 | 150 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 RELEASE-NOTES-4.3 diff --git a/DEPENDS b/DEPENDS index 61fb9586..32f1d460 100644 --- a/DEPENDS +++ b/DEPENDS @@ -22,6 +22,8 @@ │ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │ │ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │ │ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification +│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │ +│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │ └─────────────┴────────┴────────┴────────────────────────────────────────┘ ¹ libsn = libstartup-notification diff --git a/RELEASE-NOTES-4.3 b/RELEASE-NOTES-4.3 new file mode 100644 index 00000000..e7153d5a --- /dev/null +++ b/RELEASE-NOTES-4.3 @@ -0,0 +1,150 @@ + + ┌──────────────────────────────┐ + │ Release notes for i3 v4.3 │ + └──────────────────────────────┘ + +This is the third release of the new major version of i3, v4.3. It is +considered stable. All users of i3 are strongly encouraged to upgrade. + +One of the most visible changes is probably that commands which lead to an + error will now spawn i3-nagbar. This will make you immediately aware of + problems such as typos in your configuration file (such as "bindsym $mod+x exc + firefox" instead of "exec"). This is not restricted to parser errors, but all + errors (such as when trying to move a window to another workspace without + actually having a window focused). If this is annoying to you for some specific + key configuration, you can turn it off by replacing a binding like: + bindsym $mod+x move absolute position center + with something like this: + bindsym $mod+x exec --no-startup-id i3-msg move absolute position center >/dev/null 2>&1 + (Yes, this is somewhat painful, but intended. You should not suppress errors in + general, so we don’t want to make it too easy.) + + +We also made the orientation (horizontal/vertical) part of the layout + mechanism: Before, we got the default layout and you could change + orientation. Now, there are two new layouts "splitv" and "splith", which + replace the default layout. The "split h" and "split v" commands continue to + work as before, they split the current container and you will end up in a + split container with layout splith (after "split h") or splitv (after "split + v"). + + To change a splith container into a splitv container, use either "layout + splitv" or "layout toggle split". The latter command is used in the + default config as mod+l (previously "layout default"). In case you have + "layout default" in your config file, it is recommended to just replace + it by "layout toggle split", which will work as "layout default" did + before when pressing it once, but toggle between horizontal/vertical + when pressing it repeatedly. + + The rationale behind this commit is that it’s cleaner to have all + parameters that influence how windows are rendered in the layout itself + rather than having a special parameter in combination with only one + layout. This enables us to change existing split containers in all cases + without breaking existing features (see ticket #464). Also, users should + feel more confident about whether they are actually splitting or just + changing an existing split container now. + + As a nice side-effect, this commit brings back the "layout toggle" + feature we once had in i3 version 3 (see the userguide). + + +Another very important change is that we now support pango for rendering text. + The default is still to use misc-fixed (X core fonts), but you can use a font + specification starting with "xft:" now, such as "xft:DejaVu Sans Mono 10" and + i3 will use pango. The sole motivation for this is NOT to have fancier window + decorations, but to support fonts which have more glyphs (think Japanese for + example) and to support right-to-left rendering (open http://www.ftpal.net/ + for an example). Supporting users from all over the planet is important, and + as such I would strongly advise distribution packagers to leave pango support + in. In case you are working on a very low-spec embedded device, it is easy + enough to disable pango support, see common.mk. + + ┌────────────────────────────┐ + │ Changes in v4.3 │ + └────────────────────────────┘ + + • docs/refcard: update for v4 + • docs/userguide: clarify the default for focus_follows_mouse and new_window + • docs/userguide: add section about implicit containers + • docs/ipc: document the 'window' field in the GET_TREE reply + • docs/ipc: update links to ipc libraries + • docs/ipc: make the reply sections consistent + • docs/i3bar-protocol: add example (illustration-only!) shell script + • man/i3bar.man: reference i3bar-protocol + • IPC: Commands now lead to proper error messages in general. If we forgot + about a specific one, please open a ticket. + • IPC: implement GET_VERSION to find out the i3 version + • i3-dump-log now comes with a massively more helpful error message that + should cover all the use cases. + • 'workspace number ' now opens a new workspace + • 'workspace number ' now works with the back_and_forth option + • Allow focus with target (criteria) when in fullscreen mode in some cases + • Allow focus child/parent when in fullscreen mode + • Restrict directional focus when in fullscreen mode + • Prevent moving out of fullscreen containers + • Add 'move to workspace current' (useful when used with criteria) + • replace loglevels by a global debug logging + • make: new makefile layout + • make: canonicalize path when compiling. This leads to sth like + ../i3-4.2/src/main.c in backtraces, clearly identifying i3 code. + • automatically hide i3bar when it’s unneeded (after urgency hints) + • i3-config-wizard: use the level 0 keysym whenever it’s unambiguous + • i3-nagbar: use custom scripts to work around different terminal emulators + using different ways of interpreting the arguments to -e + • i3-sensible-terminal: add xfce4-terminal + • default config: require confirmation when exiting i3 + • Display i3-nagbar when a command leads to an error. + • testcases: complete-run now supports --xtrace + • testcases: handle EAGAIN (fixes hangs) + • testcases: handle test bailouts + • Introduce splith/splitv layouts, remove orientation + • Implement hide_edge_borders option + • Support _NET_ACTIVE_WINDOW ClientMessages + • Set I3_PID atom on the X11 root window + • Implement i3 --moreversion, handy for figuring out whether you run the + latest binary which is installed. + • i3bar: be less strict about the {"version":1} JSON header + • shm-logging: implement i3-dump-log -f (follow) + • Implement pango support + + ┌────────────────────────────┐ + │ Bugfixes │ + └────────────────────────────┘ + + • Fix floating precision bug when floating windows are moved between outputs. + • i3bar won’t crash when full_text is missing or null in the JSON input + • When having "workspace number 1" in your config, there will no longer be a + stray workspace "number 1". + • i3.config.keycodes used bindsym instead of bindcode for the arrow key + resizing bindings by mistake + • Fix 'move to workspace' when used with criteria + • Handle clicks to the very left edge of i3bar + • When using i3 -C, don’t send remaining arguments as an IPC command + • Fix reload crashes in rare cases + • i3bar: inform all clients of new tray selection owner (fixes tray problems + with X-Chat and possibly others) + • resizing: traverse containers up properly (fixes non-working resizing when + having a h-split within a h-split for example) + • Fix floating coordinates when moving assigned workspaces + • Properly fix floating coordinates when disabling outputs + • floating_fix_coordinates: properly deal with negative positions + • floating windows: add deco_height only when in normal border mode (fixes + initial floating window position/size when using a different default border + setting). + • Fix moving scratchpad window + • Cleanup zero-byte logfile on immediate exit (they are created by i3 + --get-socketpath for example). + + ┌────────────────────────────┐ + │ Thanks! │ + └────────────────────────────┘ + +Thanks for testing, bugfixes, discussions and everything I forgot go out to: + + aksr, Axel Wagner, darkraven, David Coppa, eeemsi, Felicitus, Fernando Tarlá + Cardoso Lemos, Iakov Davydov, jh, Julius Plenz, Marcel Hellwig, Marcus, + Michael Stapelberg, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel + Löbl, Philipp Middendorf, prg, Quentin Glidic, somelauw, stfn, tucos, + TunnelWicht, Valentin Haenel + +-- Michael Stapelberg, 2012-08-18 From be6190a516bef5280d189aa728333ce4e6e0b17a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 18 Aug 2012 16:27:00 +0200 Subject: [PATCH 138/200] complete-run: check whether Xdummy dies, add a flag to keep the Xdummy output --- testcases/complete-run.pl | 4 +++- testcases/lib/StartXDummy.pm | 26 +++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 4bdf5c78..5ea9d078 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -51,9 +51,11 @@ my %options = ( coverage => 0, restart => 0, ); +my $keep_xdummy_output = 0; my $result = GetOptions( "coverage-testing" => \$options{coverage}, + "keep-xdummy-output" => \$keep_xdummy_output, "valgrind" => \$options{valgrind}, "strace" => \$options{strace}, "xtrace" => \$options{xtrace}, @@ -77,7 +79,7 @@ my $numtests = scalar @testfiles; # No displays specified, let’s start some Xdummy instances. if (@displays == 0) { - @displays = start_xdummy($parallel, $numtests); + @displays = start_xdummy($parallel, $numtests, $keep_xdummy_output); } # 1: create an output directory for this test-run diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXDummy.pm index 5c739fca..68ca79f6 100644 --- a/testcases/lib/StartXDummy.pm +++ b/testcases/lib/StartXDummy.pm @@ -9,6 +9,7 @@ use v5.10; our @EXPORT = qw(start_xdummy); +my @pids; my $x_socketpath = '/tmp/.X11-unix/X'; # reads in a whole file @@ -20,13 +21,16 @@ sub slurp { # forks an Xdummy or Xdmx process sub fork_xserver { + my $keep_xdummy_output = shift; my $displaynum = shift; my $pid = fork(); die "Could not fork: $!" unless defined($pid); if ($pid == 0) { # Child, close stdout/stderr, then start Xdummy. - close STDOUT; - close STDERR; + if (!$keep_xdummy_output) { + close STDOUT; + close STDERR; + } exec @_; exit 1; @@ -37,6 +41,8 @@ sub fork_xserver { unlink($x_socketpath . $displaynum); }); + push @pids, $pid; + return $x_socketpath . $displaynum; } @@ -63,11 +69,20 @@ the Xdummy processes and a list of PIDs of the processes. =cut sub start_xdummy { - my ($parallel, $numtests) = @_; + my ($parallel, $numtests, $keep_xdummy_output) = @_; my @displays = (); my @childpids = (); + $SIG{CHLD} = sub { + my $child = waitpid -1, POSIX::WNOHANG; + @pids = grep { $_ != $child } @pids; + return unless @pids == 0; + print STDERR "All Xdummy processes died.\n"; + print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n"; + exit 1; + }; + # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have # _SC_NPROCESSORS_CONF. my $cpuinfo = slurp('/proc/cpuinfo'); @@ -93,8 +108,9 @@ sub start_xdummy { # We use -config /dev/null to prevent Xdummy from using the system # Xorg configuration. The tests should be independant from the # actual system X configuration. - my $socket = fork_xserver($displaynum, './Xdummy', ":$displaynum", - '-config', '/dev/null', '-nolisten', 'tcp'); + my $socket = fork_xserver($keep_xdummy_output, $displaynum, + './Xdummy', ":$displaynum", '-config', '/dev/null', + '-nolisten', 'tcp'); push(@displays, ":$displaynum"); push(@sockets_waiting, $socket); $displaynum++; From bb853660cfe31bb15d481b925e53f925f2385cea Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Mon, 20 Aug 2012 12:36:10 +0200 Subject: [PATCH 139/200] Create ws in "move workspace number n" if not existing fixes #729 --- src/commands.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/commands.c b/src/commands.c index 9830478f..8875b8d2 100644 --- a/src/commands.c +++ b/src/commands.c @@ -486,14 +486,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { child->num == parsed_num); if (!workspace) { - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - // TODO: better error message - ystr("No such workspace"); - y(map_close); - return; + workspace = workspace_get(which, NULL); } HANDLE_EMPTY_MATCH; From 7cadc40846417bbebbf929c91f59710a60164510 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 22 Aug 2012 17:16:18 +0200 Subject: [PATCH 140/200] testcase for the previous commit --- testcases/t/132-move-workspace.t | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/testcases/t/132-move-workspace.t b/testcases/t/132-move-workspace.t index 79753cd7..0f1cbcc1 100644 --- a/testcases/t/132-move-workspace.t +++ b/testcases/t/132-move-workspace.t @@ -141,4 +141,26 @@ $ws = get_ws($tmp2); is(@{$ws->{nodes}}, 0, 'no nodes on workspace'); is(@{$ws->{floating_nodes}}, 1, 'one floating node on workspace'); +################################################################################ +# Check that 'move workspace number' works correctly. +################################################################################ + +$tmp = get_unused_workspace(); +cmd 'open'; + +cmd 'workspace 16'; +cmd 'open'; +is(@{get_ws('16')->{nodes}}, 1, 'one node on ws 16'); + +cmd "workspace $tmp"; +cmd 'open'; +cmd 'move workspace number 16'; +is(@{get_ws('16')->{nodes}}, 2, 'two nodes on ws 16'); + +ok(!workspace_exists('17'), 'workspace 17 does not exist yet'); +cmd 'open'; +cmd 'move workspace number 17'; +ok(workspace_exists('17'), 'workspace 17 created by moving'); +is(@{get_ws('17')->{nodes}}, 1, 'one node on ws 16'); + done_testing; From 6e75dc7e0eca9894f315854e6497e7c26b8c889b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 22 Aug 2012 17:22:20 +0200 Subject: [PATCH 141/200] testsuite: add hint about missing DUMMY Xorg module --- testcases/lib/StartXDummy.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXDummy.pm index 68ca79f6..f2ebcadd 100644 --- a/testcases/lib/StartXDummy.pm +++ b/testcases/lib/StartXDummy.pm @@ -80,6 +80,9 @@ sub start_xdummy { return unless @pids == 0; print STDERR "All Xdummy processes died.\n"; print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n"; + print STDERR ""; + print STDERR "A frequent cause for this is missing the DUMMY Xorg module,\n"; + print STDERR "package xserver-xorg-video-dummy on Debian.\n"; exit 1; }; From eadf1e306f25e3369c968e36d8a86827adda610c Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Wed, 22 Aug 2012 17:49:22 +0200 Subject: [PATCH 142/200] Fix 'back and forth' in 'workspace number' for named ws --- src/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.c b/src/commands.c index 8875b8d2..85d8cdf4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -846,7 +846,7 @@ void cmd_workspace_number(I3_CMD, char *which) { cmd_output->needs_tree_render = true; return; } - if (maybe_back_and_forth(cmd_output, which)) + if (maybe_back_and_forth(cmd_output, workspace->name)) return; workspace_show(workspace); From 82b73c20c913b532ef191eb28570037676734acd Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 22 Aug 2012 17:57:58 +0200 Subject: [PATCH 143/200] testcase for the previous commit --- testcases/t/176-workspace-baf.t | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/testcases/t/176-workspace-baf.t b/testcases/t/176-workspace-baf.t index 80b2d471..80644563 100644 --- a/testcases/t/176-workspace-baf.t +++ b/testcases/t/176-workspace-baf.t @@ -66,12 +66,31 @@ ok(get_ws($second_ws)->{focused}, 'second workspace focused'); cmd 'workspace number 5'; ok(get_ws('5')->{focused}, 'workspace 5 focused'); +# ensure it stays open +cmd 'open'; cmd 'workspace number 6'; ok(get_ws('6')->{focused}, 'workspace 6 focused'); +# ensure it stays open +cmd 'open'; cmd 'workspace number 6'; -ok(get_ws('5')->{focused}, 'workspace 5 focused again'); +is(focused_ws, '5', 'workspace 5 focused again'); + +################################################################################ +# Rename the workspaces and see if workspace number still works with BAF. +################################################################################ + +cmd 'rename workspace 5 to 5: foo'; +cmd 'rename workspace 6 to 6: baz'; + +is(focused_ws, '5: foo', 'workspace 5 still focused'); + +cmd 'workspace number 6'; +is(focused_ws, '6: baz', 'workspace 6 now focused'); + +cmd 'workspace number 6'; +is(focused_ws, '5: foo', 'workspace 5 focused again'); exit_gracefully($pid); From d29b62f24f3e94a5ca205fd03ca9d17546d385e4 Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Wed, 22 Aug 2012 17:49:23 +0200 Subject: [PATCH 144/200] Remove dead code in cmd_workspace_number If a ws doesn't exist, maybe_back_and_forth will never return true for it. --- src/commands.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/commands.c b/src/commands.c index 85d8cdf4..00d8b97d 100644 --- a/src/commands.c +++ b/src/commands.c @@ -840,8 +840,6 @@ void cmd_workspace_number(I3_CMD, char *which) { ysuccess(true); /* terminate the which string after the endposition of the number */ *endptr = '\0'; - if (maybe_back_and_forth(cmd_output, which)) - return; workspace_show_by_name(which); cmd_output->needs_tree_render = true; return; From b9255f51f8ee9eb8a028394cf429c25c410298ef Mon Sep 17 00:00:00 2001 From: eeemsi Date: Thu, 23 Aug 2012 12:55:28 +0200 Subject: [PATCH 145/200] Use (void) instead of () for functions without args --- i3bar/include/child.h | 10 +++++----- i3bar/include/ipc.h | 6 +++--- i3bar/include/outputs.h | 4 ++-- i3bar/include/workspaces.h | 4 ++-- i3bar/include/xcb.h | 14 +++++++------- i3bar/src/child.c | 12 ++++++------ i3bar/src/ipc.c | 6 +++--- i3bar/src/outputs.c | 4 ++-- i3bar/src/workspaces.c | 4 ++-- i3bar/src/xcb.c | 24 ++++++++++++------------ 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/i3bar/include/child.h b/i3bar/include/child.h index 24c7e460..c0b56a01 100644 --- a/i3bar/include/child.h +++ b/i3bar/include/child.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * child.c: Getting Input for the statusline * @@ -24,25 +24,25 @@ void start_child(char *command); * kill()s the child-process (if any). Called when exit()ing. * */ -void kill_child_at_exit(); +void kill_child_at_exit(void); /* * kill()s the child-process (if any) and closes and * free()s the stdin- and sigchild-watchers * */ -void kill_child(); +void kill_child(void); /* * Sends a SIGSTOP to the child-process (if existent) * */ -void stop_child(); +void stop_child(void); /* * Sends a SIGCONT to the child-process (if existent) * */ -void cont_child(); +void cont_child(void); #endif diff --git a/i3bar/include/ipc.h b/i3bar/include/ipc.h index a0c49704..f20d45f0 100644 --- a/i3bar/include/ipc.h +++ b/i3bar/include/ipc.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * ipc.c: Communicating with i3 * @@ -23,7 +23,7 @@ int init_connection(const char *socket_path); * Destroy the connection to i3. * */ -void destroy_connection(); +void destroy_connection(void); /* * Sends a Message to i3. @@ -36,6 +36,6 @@ int i3_send_msg(uint32_t type, const char* payload); * Subscribe to all the i3-events, we need * */ -void subscribe_events(); +void subscribe_events(void); #endif diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index f9ddd54b..ad249786 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * outputs.c: Maintaining the output-list * @@ -29,7 +29,7 @@ void parse_outputs_json(char* json); * Initiate the output-list * */ -void init_outputs(); +void init_outputs(void); /* * Returns the output with the given name diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h index 1c77b0b3..5fe1ba1e 100644 --- a/i3bar/include/workspaces.h +++ b/i3bar/include/workspaces.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * workspaces.c: Maintaining the workspace-lists * @@ -28,7 +28,7 @@ void parse_workspaces_json(char *json); * free() all workspace data-structures * */ -void free_workspaces(); +void free_workspaces(void); struct i3_ws { int num; /* The internal number of the ws */ diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 9ed21496..6c7bc567 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * xcb.c: Communicating with X * @@ -69,13 +69,13 @@ void init_colors(const struct xcb_color_strings_t *colors); * Called once, before the program terminates. * */ -void clean_xcb(); +void clean_xcb(void); /* * Get the earlier requested atoms and save them in the prepared data-structure * */ -void get_atoms(); +void get_atoms(void); /* * Reparents all tray clients of the specified output to the root window. This @@ -98,24 +98,24 @@ void destroy_window(i3_output *output); * Reallocate the statusline-buffer * */ -void realloc_sl_buffer(); +void realloc_sl_buffer(void); /* * Reconfigure all bars and create new for newly activated outputs * */ -void reconfig_windows(); +void reconfig_windows(void); /* * Render the bars, with buttons and statusline * */ -void draw_bars(); +void draw_bars(void); /* * Redraw the bars, i.e. simply copy the buffer to the barwindow * */ -void redraw_bars(); +void redraw_bars(void); #endif diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 5cbae6a5..a5f49aa3 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * child.c: Getting Input for the statusline * @@ -56,7 +56,7 @@ char *statusline_buffer = NULL; * Stop and free() the stdin- and sigchild-watchers * */ -void cleanup() { +void cleanup(void) { if (stdin_io != NULL) { ev_io_stop(main_loop, stdin_io); FREE(stdin_io); @@ -327,7 +327,7 @@ void start_child(char *command) { * kill()s the child-process (if any). Called when exit()ing. * */ -void kill_child_at_exit() { +void kill_child_at_exit(void) { if (child_pid != 0) { kill(child_pid, SIGCONT); kill(child_pid, SIGTERM); @@ -339,7 +339,7 @@ void kill_child_at_exit() { * free()s the stdin- and sigchild-watchers * */ -void kill_child() { +void kill_child(void) { if (child_pid != 0) { kill(child_pid, SIGCONT); kill(child_pid, SIGTERM); @@ -354,7 +354,7 @@ void kill_child() { * Sends a SIGSTOP to the child-process (if existent) * */ -void stop_child() { +void stop_child(void) { if (child_pid != 0) { kill(child_pid, SIGSTOP); } @@ -364,7 +364,7 @@ void stop_child() { * Sends a SIGCONT to the child-process (if existent) * */ -void cont_child() { +void cont_child(void) { if (child_pid != 0) { kill(child_pid, SIGCONT); } diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index 41b8e151..2cc80cf7 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * ipc.c: Communicating with i3 * @@ -286,7 +286,7 @@ int init_connection(const char *socket_path) { /* * Destroy the connection to i3. */ -void destroy_connection() { +void destroy_connection(void) { close(i3_connection->fd); ev_io_stop(main_loop, i3_connection); } @@ -295,7 +295,7 @@ void destroy_connection() { * Subscribe to all the i3-events, we need * */ -void subscribe_events() { +void subscribe_events(void) { if (config.disable_ws) { i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]"); } else { diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index eabf4d7b..db986702 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * outputs.c: Maintaining the output-list * @@ -266,7 +266,7 @@ yajl_callbacks outputs_callbacks = { * Initiate the output-list * */ -void init_outputs() { +void init_outputs(void) { outputs = smalloc(sizeof(struct outputs_head)); SLIST_INIT(outputs); } diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index 6db37983..5e01b98d 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * workspaces.c: Maintaining the workspace-lists * @@ -262,7 +262,7 @@ void parse_workspaces_json(char *json) { * free() all workspace data-structures. Does not free() the heads of the tailqueues. * */ -void free_workspaces() { +void free_workspaces(void) { i3_output *outputs_walk; if (outputs == NULL) { return; diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 1d6a4d2b..861925b9 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3bar - an xcb-based status- and ws-bar for i3 - * © 2010-2011 Axel Wagner and contributors (see also: LICENSE) + * © 2010-2012 Axel Wagner and contributors (see also: LICENSE) * * xcb.c: Communicating with X * @@ -108,7 +108,7 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) { * Redraws the statusline to the buffer * */ -void refresh_statusline() { +void refresh_statusline(void) { struct status_block *block; uint32_t old_statusline_width = statusline_width; @@ -161,7 +161,7 @@ void refresh_statusline() { * Hides all bars (unmaps them) * */ -void hide_bars() { +void hide_bars(void) { if (!config.hide_on_modifier) { return; } @@ -180,7 +180,7 @@ void hide_bars() { * Unhides all bars (maps them) * */ -void unhide_bars() { +void unhide_bars(void) { if (!config.hide_on_modifier) { return; } @@ -353,7 +353,7 @@ void handle_button(xcb_button_press_event_t *event) { * new tray client or removing an old one. * */ -static void configure_trayclients() { +static void configure_trayclients(void) { trayclient *trayclient; i3_output *output; SLIST_FOREACH(output, outputs, slist) { @@ -966,7 +966,7 @@ void init_xcb_late(char *fontname) { * atom. Afterwards, tray clients will send ClientMessages to our window. * */ -void init_tray() { +void init_tray(void) { DLOG("Initializing system tray functionality\n"); /* request the tray manager atom for the X11 display we are running on */ char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11]; @@ -1055,7 +1055,7 @@ void init_tray() { * Called once, before the program terminates. * */ -void clean_xcb() { +void clean_xcb(void) { i3_output *o_walk; free_workspaces(); SLIST_FOREACH(o_walk, outputs, slist) { @@ -1083,7 +1083,7 @@ void clean_xcb() { * Get the earlier requested atoms and save them in the prepared data structure * */ -void get_atoms() { +void get_atoms(void) { xcb_intern_atom_reply_t *reply; #define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \ if (reply == NULL) { \ @@ -1145,7 +1145,7 @@ void destroy_window(i3_output *output) { * Reallocate the statusline-buffer * */ -void realloc_sl_buffer() { +void realloc_sl_buffer(void) { DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n", statusline_width, root_screen->width_in_pixels); xcb_free_pixmap(xcb_connection, statusline_pm); @@ -1189,7 +1189,7 @@ void realloc_sl_buffer() { * Reconfigure all bars and create new bars for recently activated outputs * */ -void reconfig_windows() { +void reconfig_windows(void) { uint32_t mask; uint32_t values[5]; static bool tray_configured = false; @@ -1398,7 +1398,7 @@ void reconfig_windows() { * Render the bars, with buttons and statusline * */ -void draw_bars() { +void draw_bars(void) { DLOG("Drawing Bars...\n"); int i = 0; @@ -1537,7 +1537,7 @@ void draw_bars() { * Redraw the bars, i.e. simply copy the buffer to the barwindow * */ -void redraw_bars() { +void redraw_bars(void) { i3_output *outputs_walk; SLIST_FOREACH(outputs_walk, outputs, slist) { if (!outputs_walk->active) { From c8cfc77b0a7c6166641b1c5849e4183ef97d600d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 23 Aug 2012 20:27:08 +0200 Subject: [PATCH 146/200] docs/testsuite: mention why using a separate language is great --- docs/testsuite | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/testsuite b/docs/testsuite index 720ff394..fcc9393a 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -45,6 +45,8 @@ For several reasons, the i3 testsuite has been implemented in Perl: 2. Perl is widely available and has a well-working package infrastructure. 3. The author is familiar with Perl :). +4. It is a good idea to use a different language for the tests than the + implementation itself. Please do not start programming language flamewars at this point. From 67999269fd562c0be3de0ce360a10dd6c38b8bc6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 27 Aug 2012 15:07:18 +0200 Subject: [PATCH 147/200] =?UTF-8?q?manpage:=20improve=20locale=20setup=20i?= =?UTF-8?q?n=20example=20.xsession=20(Thanks=20Harald=20K=C3=B6nig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Harald mentioned he was surprised about the locales we recommend in the .xsession example, so I’ve re-investigated. Here is the test program I have used: #include #include int main() { /* SUSv2 setlocale(3) says: * Internationalised programs must call setlocale() to initiate * a specific language operation. This can be done by calling * setlocale() as follows: */ setlocale(LC_ALL, ""); printf("LC_NUMERIC is %s\n", setlocale(LC_NUMERIC, NULL)); } Then, I have unset LANG and LC_*: midna /tmp $ env | grep LANG midna /tmp $ env | grep LC midna /tmp $ Now, observe that LC_ALL overwrites all specific LC variables: midna /tmp $ LC_ALL=de_DE.UTF-8 LC_NUMERIC=en_DK.UTF-8 ./localetest LC_NUMERIC is de_DE.UTF-8 However, LANG does not: midna /tmp $ LANG=de_DE.UTF-8 ./localetest LC_NUMERIC is de_DE.UTF-8 midna /tmp $ LANG=de_DE.UTF-8 LC_NUMERIC=en_DK.UTF-8 ./localetest LC_NUMERIC is en_DK.UTF-8 This is consistent with what perldoc perllocale says: http://perldoc.perl.org/perllocale.html#ENVIRONMENT --- man/i3.man | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/man/i3.man b/man/i3.man index c20e39da..c2b9039c 100644 --- a/man/i3.man +++ b/man/i3.man @@ -262,19 +262,15 @@ xset -b # Enable zapping (C-A- kills X) setxkbmap -option terminate:ctrl_alt_bksp -# Enforce correct locales from the beginning -unset LC_COLLATE -export LC_CTYPE=de_DE.UTF-8 -export LC_TIME=de_DE.UTF-8 -export LC_NUMERIC=de_DE.UTF-8 -export LC_MONETARY=de_DE.UTF-8 +# Enforce correct locales from the beginning: +# LC_ALL is unset since it overwrites everything +# LANG=de_DE.UTF-8 is used, except for: +# LC_MESSAGES=C never translates program output +# LC_TIME=en_DK leads to yyyy-mm-dd hh:mm date/time output +unset LC_ALL +export LANG=de_DE.UTF-8 export LC_MESSAGES=C -export LC_PAPER=de_DE.UTF-8 -export LC_NAME=de_DE.UTF-8 -export LC_ADDRESS=de_DE.UTF-8 -export LC_TELEPHONE=de_DE.UTF-8 -export LC_MEASUREMENT=de_DE.UTF-8 -export LC_IDENTIFICATION=de_DE.UTF-8 +export LC_TIME=en_DK.UTF-8 # Use XToolkit in java applications export AWT_TOOLKIT=XToolkit From c97d6198bd9a868394c46aa052313ba5ec7b69d9 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 27 Aug 2012 15:22:19 +0200 Subject: [PATCH 148/200] manpage: describe keybindings to open a terminal/dmenu (Thanks Joost) --- man/i3.man | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/i3.man b/man/i3.man index c2b9039c..bc4a0187 100644 --- a/man/i3.man +++ b/man/i3.man @@ -92,6 +92,12 @@ are connected to these outputs. Here is a short overview of the default keybindings: +Mod1+Enter:: +Open a new terminal emulator window. + +Mod1+d:: +Open dmenu for starting any application by typing (part of) its name. + j/k/l/;:: Direction keys (left, down, up, right). They are on your homerow (see the mark on your "j" key). Alternatively, you can use the cursor keys. From b8ac9591a295310d5f458ea9d9d01bc972364694 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 27 Aug 2012 15:25:22 +0200 Subject: [PATCH 149/200] manpage: update force-xinerama nvidia hint --- man/i3.man | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/i3.man b/man/i3.man index bc4a0187..096a359b 100644 --- a/man/i3.man +++ b/man/i3.man @@ -1,7 +1,7 @@ i3(1) ===== -Michael Stapelberg -v4.0, July 2011 +Michael Stapelberg +v4.2, August 2012 == NAME @@ -48,8 +48,8 @@ Please be aware that i3 is primarily targeted at advanced users and developers. === IMPORTANT NOTE TO nVidia BINARY DRIVER USERS If you are using the nVidia binary graphics driver (also known as 'blob') -you need to use the +--force-xinerama+ flag (in your ~/.xsession) when starting -i3, like so: +before version 302.17, you need to use the +--force-xinerama+ flag (in your +~/.xsession) when starting i3, like so: ---------------------------------------------- exec i3 --force-xinerama -V >>~/.i3/i3log 2>&1 From af15087b995f912f68eab21ce7602ea71e79dfd1 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sat, 25 Aug 2012 23:44:13 +0200 Subject: [PATCH 150/200] i3-input: Do not use a non-loaded font We must call load_font before any font-related calls like predict_text_width --- i3-input/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index 3172387d..fbc551e6 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -353,9 +353,6 @@ int main(int argc, char *argv[]) { sockfd = ipc_connect(socket_path); - if (prompt != NULL) - prompt_offset = predict_text_width(prompt); - int screens; conn = xcb_connect(NULL, &screens); if (!conn || xcb_connection_has_error(conn)) @@ -369,6 +366,9 @@ int main(int argc, char *argv[]) { font = load_font(pattern, true); set_font(&font); + if (prompt != NULL) + prompt_offset = predict_text_width(prompt); + /* Open an input window */ win = xcb_generate_id(conn); xcb_create_window( From a007283773b9611e9f50dd6dff49fae0069b520d Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Tue, 28 Aug 2012 11:44:09 +0200 Subject: [PATCH 151/200] i3-input: Fix text drawing offset --- i3-input/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3-input/main.c b/i3-input/main.c index fbc551e6..b3e626e6 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -125,7 +125,7 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t if (input_position > 0) { i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); - draw_text(input, pixmap, pixmap_gc, 4, 4, 492); + draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492); i3string_free(input); } From 6d8f61205d41d5e89235dbb7d4d0c3f1c9f27c26 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 28 Aug 2012 13:26:12 +0200 Subject: [PATCH 152/200] accept slashes in RandR output names (+test) (Thanks dbp) fixes #785 --- src/cfgparse.l | 2 +- testcases/t/196-randr-output-names.t | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 testcases/t/196-randr-output-names.t diff --git a/src/cfgparse.l b/src/cfgparse.l index 04117624..52cde189 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -159,7 +159,7 @@ EOL (\r?\n) return STR; } [^\n]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return STR; } -[a-zA-Z0-9_-]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return OUTPUT; } +[a-zA-Z0-9\/_-]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return OUTPUT; } ^[ \t]*#[^\n]* { return TOKCOMMENT; } #[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return HEXCOLOR; } {EOL} { diff --git a/testcases/t/196-randr-output-names.t b/testcases/t/196-randr-output-names.t new file mode 100644 index 00000000..dfc288d9 --- /dev/null +++ b/testcases/t/196-randr-output-names.t @@ -0,0 +1,22 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Verify that i3 allows strange RandR output names such as DVI-I_1/digital. +# Ticket: #785 +# Bug still in: 4.2-256-ga007283 +use i3test i3_autostart => 0; +use File::Temp qw(tempfile); + +my ($fh, $filename) = tempfile(UNLINK => 1); +print $fh < Date: Thu, 30 Aug 2012 21:12:22 +0100 Subject: [PATCH 153/200] Make the initial mention of 'split h/v' a little easier to understand. --- docs/userguide | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide b/docs/userguide index 74e5fc59..bc894800 100644 --- a/docs/userguide +++ b/docs/userguide @@ -61,8 +61,8 @@ windows. TODO: picture of the tree -To split a window vertically, press +mod+v+. To split it horizontally, press -+mod+h+. +To split a window vertically, press +mod+v+ before you create the new window. +To split it horizontally, press +mod+h+. === Changing the container layout From 48baf3bf65e849a1f7e6ee205533544deecf13ac Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 31 Aug 2012 00:17:06 +0200 Subject: [PATCH 154/200] testcases: add ExtUtils::PkgConfig as explicit dependency This used to be a transitive dependency through X11::XCB, but the latter dropped it in version 0.04. --- testcases/Makefile.PL | 1 + 1 file changed, 1 insertion(+) diff --git a/testcases/Makefile.PL b/testcases/Makefile.PL index 1c987389..b1e698ae 100755 --- a/testcases/Makefile.PL +++ b/testcases/Makefile.PL @@ -11,6 +11,7 @@ WriteMakefile( 'AnyEvent::I3' => '0.09', 'X11::XCB' => '0.03', 'Inline' => 0, + 'ExtUtils::PkgConfig' => 0, 'Test::More' => '0.94', }, PM => {}, # do not install any files from this directory From 2d1ebc2b90abda7f5af636babf3a33920a6963b3 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 3 Sep 2012 00:41:15 +0200 Subject: [PATCH 155/200] Bugfix: memleak: because we use i3string_from_utf8(), we need to free buffer() Previously, buffer was directly used and thus not freed, but i3string_from_utf8() makes a copy. --- i3bar/src/child.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3bar/src/child.c b/i3bar/src/child.c index a5f49aa3..4f7a8707 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -217,7 +217,6 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n", status, rec, json_input); } - free(buffer); } else { struct status_block *first = TAILQ_FIRST(&statusline_head); /* Clear the old buffer if any. */ @@ -229,6 +228,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { else buffer[rec] = '\0'; first->full_text = i3string_from_utf8((const char *)buffer); } + free(buffer); draw_bars(); } From f18ab28f5ca7428d112d25d839b63a3bc4762d14 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 3 Sep 2012 00:41:51 +0200 Subject: [PATCH 156/200] bugfix: memleak: use i3STRING_FREE() instead of FREE() --- i3bar/src/child.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 4f7a8707..058ddb7a 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -80,7 +80,7 @@ static int stdin_start_array(void *context) { struct status_block *first; while (!TAILQ_EMPTY(&statusline_head)) { first = TAILQ_FIRST(&statusline_head); - FREE(first->full_text); + I3STRING_FREE(first->full_text); FREE(first->color); TAILQ_REMOVE(&statusline_head, first, blocks); free(first); From 4636eb840de16d60363c133450499f584fb9b2fe Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 3 Sep 2012 14:55:27 +0200 Subject: [PATCH 157/200] fix compilation with older xcb-util with -DXCB_COMPAT (Thanks okraits) --- common.mk | 1 + i3-config-wizard/i3-config-wizard.mk | 2 +- i3-dump-log/i3-dump-log.mk | 2 +- i3-input/i3-input.mk | 2 +- i3-msg/i3-msg.mk | 2 +- i3-nagbar/i3-nagbar.mk | 2 +- i3bar/i3bar.mk | 2 +- libi3/libi3.mk | 2 +- src/i3.mk | 8 ++++---- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/common.mk b/common.mk index 989ed5ea..8de425d0 100644 --- a/common.mk +++ b/common.mk @@ -88,6 +88,7 @@ XCB_CFLAGS += $(call cflags_for_lib, xcb-atom) XCB_CFLAGS += $(call cflags_for_lib, xcb-aux) XCB_LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom) XCB_LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) +XCB_CPPFLAGS+= -DXCB_COMPAT else XCB_CFLAGS += $(call cflags_for_lib, xcb-util) XCB_LIBS += $(call ldflags_for_lib, xcb-util) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 7e9c4bee..1598cfed 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -13,7 +13,7 @@ i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_con i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS) echo "[i3-config-wizard] LEX $<" diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index e696546d..bbce356f 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -12,7 +12,7 @@ i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) echo "[i3-dump-log] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) echo "[i3-dump-log] Link i3-dump-log" diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index d31a11ff..03f4e0a6 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -12,7 +12,7 @@ i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) echo "[i3-input] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-input/i3-input: libi3.a $(i3_input_OBJECTS) echo "[i3-input] Link i3-input" diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index c3a59303..fef9581d 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -12,7 +12,7 @@ i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) echo "[i3-msg] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) echo "[i3-msg] Link i3-msg" diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 4fea1629..e54aa654 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -12,7 +12,7 @@ i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) echo "[i3-nagbar] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) echo "[i3-nagbar] Link i3-nagbar" diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index c311adfe..06780250 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -12,7 +12,7 @@ i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) echo "[i3bar] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< i3bar/i3bar: libi3.a $(i3bar_OBJECTS) echo "[i3bar] Link i3bar" diff --git a/libi3/libi3.mk b/libi3/libi3.mk index b6a90995..d99bacf4 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -10,7 +10,7 @@ libi3_OBJECTS := $(libi3_SOURCES:.c=.o) libi3/%.o: libi3/%.c $(libi3_HEADERS) echo "[libi3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< libi3.a: $(libi3_OBJECTS) echo "[libi3] AR libi3.a" diff --git a/src/i3.mk b/src/i3.mk index bc9eabe8..b5c7c477 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -31,11 +31,11 @@ canonical_path := ../$(shell basename $(shell readlink -f .)) include/all.h.pch: $(i3_HEADERS) echo "[i3] PCH all.h" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch src/%.o: src/%.c $(i3_HEADERS_DEP) echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$< src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS_DEP) echo "[i3] LEX $<" @@ -50,8 +50,8 @@ src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS_DEP) # and once as an object file for i3. src/commands_parser.o: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) $(i3_LIBS) - $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(LIBS) $(i3_LIBS) + $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec echo "[i3] Generating command parser" From 9976e7461e7444ed9df8562c8dbe2079ab1e7824 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 4 Sep 2012 16:53:06 +0200 Subject: [PATCH 158/200] =?UTF-8?q?remove=20xcb-proto=20from=20DEPENDS,=20?= =?UTF-8?q?it=E2=80=99s=20only=20a=20build-time=20dep=20of=20libxcb=20(Tha?= =?UTF-8?q?nks=20okraits)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEPENDS | 1 - 1 file changed, 1 deletion(-) diff --git a/DEPENDS b/DEPENDS index 32f1d460..35a03f25 100644 --- a/DEPENDS +++ b/DEPENDS @@ -8,7 +8,6 @@ │ dependency │ min. │ lkgv │ URL │ ├─────────────┼────────┼────────┼────────────────────────────────────────┤ │ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │ -│ xcb-proto │ 1.3 │ 1.6 │ http://xcb.freedesktop.org/dist/ │ │ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │ │ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │ │ libev │ 4.0 │ 4.04 │ http://libev.schmorp.de/ │ From 73d5dcbbafddf4fd31350df7b56ae0e8bdd20128 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 3 Sep 2012 22:33:35 +0200 Subject: [PATCH 159/200] x: grab server and process pending events before managing existing windows fixes #782 --- src/main.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index cc83851c..7936e758 100644 --- a/src/main.c +++ b/src/main.c @@ -755,7 +755,29 @@ int main(int argc, char *argv[]) { xcb_flush(conn); - manage_existing_windows(root); + /* What follows is a fugly consequence of X11 protocol race conditions like + * the following: In an i3 in-place restart, i3 will reparent all windows + * to the root window, then exec() itself. In the new process, it calls + * manage_existing_windows. However, in case any application sent a + * generated UnmapNotify message to the WM (as GIMP does), this message + * will be handled by i3 *after* managing the window, thus i3 thinks the + * window just closed itself. In reality, the message was sent in the time + * period where i3 wasn’t running yet. + * + * To prevent this, we grab the server (disables processing of any other + * connections), then discard all pending events (since we didn’t do + * anything, there cannot be any meaningful responses), then ungrab the + * server. */ + xcb_grab_server(conn); + { + xcb_aux_sync(conn); + xcb_generic_event_t *event; + while ((event = xcb_poll_for_event(conn)) != NULL) { + free(event); + } + manage_existing_windows(root); + } + xcb_ungrab_server(conn); struct sigaction action; From 232d6ccce9282795f30c4b64408865e5af3c9905 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 4 Sep 2012 22:24:13 +0200 Subject: [PATCH 160/200] tests: introduce is_num_children test This makes it clearer what the tests are actually doing and kills quite a bit of useless repetitions --- testcases/lib/i3test.pm | 1 + testcases/lib/i3test/Test.pm | 63 +++++++++++++++++++ testcases/t/119-match.t | 32 +++------- testcases/t/124-move.t | 9 +-- testcases/t/132-move-workspace.t | 44 ++++++------- testcases/t/175-startup-notification.t | 15 +++-- testcases/t/179-regress-multiple-ws.t | 6 +- testcases/t/186-regress-assign-focus-parent.t | 16 ++--- testcases/t/190-scratchpad-diff-ws.t | 3 +- testcases/t/501-scratchpad.t | 16 ++--- testcases/t/505-scratchpad-resolution.t | 8 +-- 11 files changed, 123 insertions(+), 90 deletions(-) create mode 100644 testcases/lib/i3test/Test.pm diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index aff23009..52deebc4 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -116,6 +116,7 @@ use Test::More $test_more_args; use Data::Dumper; use AnyEvent::I3; use Time::HiRes qw(sleep); +use i3test::Test; __ $tester->BAIL_OUT("$@") if $@; feature->import(":5.10"); diff --git a/testcases/lib/i3test/Test.pm b/testcases/lib/i3test/Test.pm new file mode 100644 index 00000000..d83de55b --- /dev/null +++ b/testcases/lib/i3test/Test.pm @@ -0,0 +1,63 @@ +package i3test::Test; + +use base 'Test::Builder::Module'; + +our @EXPORT = qw(is_num_children); + +my $CLASS = __PACKAGE__; + +=head1 NAME + +i3test::Test - Additional test instructions for use in i3 testcases + +=head1 SYNOPSIS + + use i3test; + + my $ws = fresh_workspace; + is_num_children($ws, 0, 'no containers on this workspace yet'); + cmd 'open'; + is_num_children($ws, 1, 'one container after "open"'); + + done_testing; + +=head1 DESCRIPTION + +This module provides convenience methods for i3 testcases. If you notice that a +certain pattern is present in 5 or more test cases, it should most likely be +moved into this module. + +=head1 EXPORT + +=head2 is_num_children($workspace, $expected, $test_name) + +Gets the number of children on the given workspace and verifies that they match +the expected amount of children. + + is_num_children('1', 0, 'no containers on workspace 1 at startup'); + +=cut + +sub is_num_children { + my ($workspace, $num_children, $name) = @_; + my $tb = $CLASS->builder; + + my $con = i3test::get_ws($workspace); + $tb->ok(defined($con), "Workspace $workspace exists"); + if (!defined($con)) { + $tb->skip("Workspace does not exist, skipping is_num_children"); + return; + } + + my $got_num_children = scalar @{$con->{nodes}}; + + $tb->is_num($got_num_children, $num_children, $name); +} + +=head1 AUTHOR + +Michael Stapelberg + +=cut + +1 diff --git a/testcases/t/119-match.t b/testcases/t/119-match.t index e6a4e832..b02cc1e0 100644 --- a/testcases/t/119-match.t +++ b/testcases/t/119-match.t @@ -26,8 +26,7 @@ my $win = $content->[0]; cmd q|[class=".*"] kill|; cmd q|[con_id="99999"] kill|; -$content = get_ws_content($tmp); -ok(@{$content} == 1, 'window still there'); +is_num_children($tmp, 1, 'window still there'); # now kill the window cmd 'nop now killing the window'; @@ -37,8 +36,7 @@ cmd qq|[con_id="$id"] kill|; wait_for_unmap $window; cmd 'nop checking if its gone'; -$content = get_ws_content($tmp); -ok(@{$content} == 0, 'window killed'); +is_num_children($tmp, 0, 'window killed'); # TODO: same test, but with pcre expressions @@ -86,15 +84,13 @@ my $right = open_special(name => 'right'); ok($right->mapped, 'right window mapped'); # two windows should be here -$content = get_ws_content($tmp); -ok(@{$content} == 2, 'two windows opened'); +is_num_children($tmp, 2, 'two windows opened'); cmd '[class="special" title="left"] kill'; sync_with_i3; -$content = get_ws_content($tmp); -is(@{$content}, 1, 'one window still there'); +is_num_children($tmp, 1, 'one window still there'); ###################################################################### # check that regular expressions work @@ -104,17 +100,11 @@ $tmp = fresh_workspace; $left = open_special(name => 'left', wm_class => 'special7'); ok($left->mapped, 'left window mapped'); - -# two windows should be here -$content = get_ws_content($tmp); -ok(@{$content} == 1, 'window opened'); +is_num_children($tmp, 1, 'window opened'); cmd '[class="^special[0-9]$"] kill'; - wait_for_unmap $left; - -$content = get_ws_content($tmp); -is(@{$content}, 0, 'window killed'); +is_num_children($tmp, 0, 'window killed'); ###################################################################### # check that UTF-8 works when matching @@ -124,16 +114,10 @@ $tmp = fresh_workspace; $left = open_special(name => 'ä 3', wm_class => 'special7'); ok($left->mapped, 'left window mapped'); - -# two windows should be here -$content = get_ws_content($tmp); -ok(@{$content} == 1, 'window opened'); +is_num_children($tmp, 1, 'window opened'); cmd '[title="^\w [3]$"] kill'; - wait_for_unmap $left; - -$content = get_ws_content($tmp); -is(@{$content}, 0, 'window killed'); +is_num_children($tmp, 0, 'window killed'); done_testing; diff --git a/testcases/t/124-move.t b/testcases/t/124-move.t index 052cdbff..c093e47b 100644 --- a/testcases/t/124-move.t +++ b/testcases/t/124-move.t @@ -100,14 +100,12 @@ is($nodes->[1]->{id}, $second, 'second container on bottom'); # move it outside again cmd 'move left'; -$content = get_ws_content($tmp); -is(@{$content}, 3, 'three nodes on this workspace'); +is_num_children($tmp, 3, 'three containers after moving left'); # due to automatic flattening/cleanup, the remaining split container # will be replaced by the con itself, so we will still have 3 nodes cmd 'move right'; -$content = get_ws_content($tmp); -is(@{$content}, 2, 'two nodes on this workspace'); +is_num_children($tmp, 2, 'two containers after moving right (flattening)'); ###################################################################### # 4) We create two v-split containers on the workspace, then we move @@ -128,8 +126,7 @@ cmd "move right"; cmd 'focus left'; cmd "move right"; -$content = get_ws_content($otmp); -is(@{$content}, 1, 'only one nodes on this workspace'); +is_num_children($otmp, 1, 'only one node on this workspace'); ###################################################################### # 5) test moving floating containers. diff --git a/testcases/t/132-move-workspace.t b/testcases/t/132-move-workspace.t index 0f1cbcc1..c56a17a4 100644 --- a/testcases/t/132-move-workspace.t +++ b/testcases/t/132-move-workspace.t @@ -18,20 +18,20 @@ sub move_workspace_test { my $tmp2 = get_unused_workspace(); cmd "workspace $tmp"; - ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); + is_num_children($tmp, 0, 'no containers yet'); my $first = open_empty_con($i3); my $second = open_empty_con($i3); - ok(@{get_ws_content($tmp)} == 2, 'two containers on first ws'); + is_num_children($tmp, 2, 'two containers on first ws'); cmd "workspace $tmp2"; - ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws yet'); + is_num_children($tmp2, 0, 'no containers on second ws yet'); cmd "workspace $tmp"; cmd "$movecmd $tmp2"; - ok(@{get_ws_content($tmp)} == 1, 'one container on first ws anymore'); - ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws'); + is_num_children($tmp, 1, 'one container on first ws anymore'); + is_num_children($tmp2, 1, 'one container on second ws'); my ($nodes, $focus) = get_ws_content($tmp2); is($focus->[0], $second, 'same container on different ws'); @@ -53,7 +53,7 @@ move_workspace_test('move container to workspace'); cmd 'workspace 13: meh'; cmd 'open'; -ok(@{get_ws_content('13: meh')} == 1, 'one container on 13: meh'); +is_num_children('13: meh', 1, 'one container on 13: meh'); ok(!workspace_exists('13'), 'workspace 13 does not exist yet'); @@ -61,8 +61,8 @@ cmd 'workspace 12'; cmd 'open'; cmd 'move to workspace number 13'; -ok(@{get_ws_content('13: meh')} == 2, 'two containers on 13: meh'); -ok(@{get_ws_content('12')} == 0, 'no container on 12 anymore'); +is_num_children('13: meh', 2, 'one container on 13: meh'); +is_num_children('12', 0, 'no container on 12 anymore'); ok(!workspace_exists('13'), 'workspace 13 does still not exist'); @@ -76,28 +76,28 @@ ok(!workspace_exists('13'), 'workspace 13 does still not exist'); my $tmp = get_unused_workspace(); my $tmp2 = get_unused_workspace(); cmd "workspace $tmp"; -ok(@{get_ws_content($tmp)} == 0, 'no containers yet'); +is_num_children($tmp, 0, 'no containers yet'); my $first = open_empty_con($i3); my $second = open_empty_con($i3); -ok(@{get_ws_content($tmp)} == 2, 'two containers on first ws'); +is_num_children($tmp, 2, 'two containers'); cmd "workspace $tmp2"; -ok(@{get_ws_content($tmp2)} == 0, 'no containers yet'); +is_num_children($tmp2, 0, 'no containers yet'); my $third = open_empty_con($i3); -ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws'); +is_num_children($tmp2, 1, 'one container on second ws'); # go back to the first workspace, move one of the containers to the next one cmd "workspace $tmp"; cmd 'move workspace next'; -ok(@{get_ws_content($tmp)} == 1, 'one container on first ws'); -ok(@{get_ws_content($tmp2)} == 2, 'two containers on second ws'); +is_num_children($tmp, 1, 'one container on first ws'); +is_num_children($tmp2, 2, 'two containers on second ws'); # go to the second workspace and move two containers to the first one cmd "workspace $tmp2"; cmd 'move workspace prev'; cmd 'move workspace prev'; -ok(@{get_ws_content($tmp)} == 3, 'three containers on first ws'); -ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws'); +is_num_children($tmp, 3, 'three containers on first ws'); +is_num_children($tmp2, 0, 'no containers on second ws'); ################################################################### # check if 'move workspace current' works @@ -108,16 +108,16 @@ $tmp2 = get_unused_workspace(); cmd "workspace $tmp"; $first = open_window(name => 'win-name'); -ok(@{get_ws_content($tmp)} == 1, 'one container on first ws'); +is_num_children($tmp, 1, 'one container on first ws'); cmd "workspace $tmp2"; -ok(@{get_ws_content($tmp2)} == 0, 'no containers yet'); +is_num_children($tmp2, 0, 'no containers yet'); cmd qq|[title="win-name"] move workspace $tmp2|; -ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws'); +is_num_children($tmp2, 1, 'one container on second ws'); cmd qq|[title="win-name"] move workspace $tmp|; -ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws'); +is_num_children($tmp2, 0, 'no containers on second ws'); ################################################################### # check if floating cons are moved to new workspaces properly @@ -150,12 +150,12 @@ cmd 'open'; cmd 'workspace 16'; cmd 'open'; -is(@{get_ws('16')->{nodes}}, 1, 'one node on ws 16'); +is_num_children('16', 1, 'one node on ws 16'); cmd "workspace $tmp"; cmd 'open'; cmd 'move workspace number 16'; -is(@{get_ws('16')->{nodes}}, 2, 'two nodes on ws 16'); +is_num_children('16', 2, 'two nodes on ws 16'); ok(!workspace_exists('17'), 'workspace 17 does not exist yet'); cmd 'open'; diff --git a/testcases/t/175-startup-notification.t b/testcases/t/175-startup-notification.t index 3a4dbc81..2c6ce353 100644 --- a/testcases/t/175-startup-notification.t +++ b/testcases/t/175-startup-notification.t @@ -58,7 +58,7 @@ END_OF_C_CODE my $first_ws = fresh_workspace; -is(@{get_ws_content($first_ws)}, 0, 'no containers on this workspace yet'); +is_num_children($first_ws, 0, 'no containers on this workspace yet'); ###################################################################### # 1) initiate startup, switch workspace, create window @@ -95,7 +95,7 @@ is(get_startup_id(), $startup_id, 'libstartup-notification returns the same id') my $second_ws = fresh_workspace; -is(@{get_ws_content($second_ws)}, 0, 'no containers on the second workspace yet'); +is_num_children($second_ws, 0, 'no containers on the second workspace yet'); my $win = open_window({ dont_map => 1 }); mark_window($win->id); @@ -105,8 +105,8 @@ $win->map; # We sync with i3 here to make sure $x->input_focus is updated. sync_with_i3; -is(@{get_ws_content($second_ws)}, 0, 'still no containers on the second workspace'); -is(@{get_ws_content($first_ws)}, 1, 'one container on the first workspace'); +is_num_children($second_ws, 0, 'still no containers on the second workspace'); +is_num_children($first_ws, 1, 'one container on the first workspace'); ###################################################################### # same thing, but with _NET_STARTUP_ID set on the leader @@ -119,8 +119,8 @@ $win = open_window({ dont_map => 1, client_leader => $leader }); $win->map; sync_with_i3; -is(@{get_ws_content($second_ws)}, 0, 'still no containers on the second workspace'); -is(@{get_ws_content($first_ws)}, 2, 'two containers on the first workspace'); +is_num_children($second_ws, 0, 'still no containers on the second workspace'); +is_num_children($first_ws, 2, 'two containers on the first workspace'); ###################################################################### # 2) open another window after the startup process is completed @@ -131,7 +131,7 @@ complete_startup(); sync_with_i3; my $otherwin = open_window; -is(@{get_ws_content($second_ws)}, 1, 'one container on the second workspace'); +is_num_children($second_ws, 1, 'one container on the second workspace'); ###################################################################### # 3) test that the --no-startup-id flag for exec leads to no DESKTOP_STARTUP_ID @@ -166,5 +166,4 @@ unlink($tmp); is($startup_id, '', 'startup_id empty'); - done_testing; diff --git a/testcases/t/179-regress-multiple-ws.t b/testcases/t/179-regress-multiple-ws.t index 21271170..be3cc687 100644 --- a/testcases/t/179-regress-multiple-ws.t +++ b/testcases/t/179-regress-multiple-ws.t @@ -15,11 +15,11 @@ my $keep_open_con = open_empty_con($i3); my $tmp = fresh_workspace; my $con = open_empty_con($i3); -is(@{get_ws_content($tmp)}, 1, 'one container'); -is(@{get_ws_content($old)}, 1, 'one container on old ws'); +is_num_children($tmp, 1, 'one container'); +is_num_children($old, 1, 'one container on old ws'); cmd 'move workspace prev; workspace prev'; -is(@{get_ws_content($old)}, 2, 'container moved away'); +is_num_children($old, 2, 'container moved away'); done_testing; diff --git a/testcases/t/186-regress-assign-focus-parent.t b/testcases/t/186-regress-assign-focus-parent.t index 6f2e584f..4f4db430 100644 --- a/testcases/t/186-regress-assign-focus-parent.t +++ b/testcases/t/186-regress-assign-focus-parent.t @@ -22,29 +22,21 @@ my $i3 = i3(get_socket_path(0)); cmd 'workspace targetws'; open_window(name => "testcase"); - -my $nodes = get_ws_content('targetws'); -is(scalar @$nodes, 1, 'precisely one window'); +is_num_children('targetws', 1, 'precisely one window'); open_window(name => "testcase"); - -$nodes = get_ws_content('targetws'); -is(scalar @$nodes, 2, 'precisely two windows'); +is_num_children('targetws', 2, 'precisely two windows'); cmd 'split v'; open_window(name => "testcase"); - -$nodes = get_ws_content('targetws'); -is(scalar @$nodes, 2, 'still two windows'); +is_num_children('targetws', 2, 'still two windows'); # focus parent. the new window should now be opened right next to the last one. cmd 'focus parent'; open_window(name => "testcase"); - -$nodes = get_ws_content('targetws'); -is(scalar @$nodes, 3, 'new window opened next to last one'); +is_num_children('targetws', 3, 'new window opened next to last one'); exit_gracefully($pid); diff --git a/testcases/t/190-scratchpad-diff-ws.t b/testcases/t/190-scratchpad-diff-ws.t index 9b6e6c7a..ed03f210 100644 --- a/testcases/t/190-scratchpad-diff-ws.t +++ b/testcases/t/190-scratchpad-diff-ws.t @@ -45,8 +45,7 @@ my $win = open_window; my $scratch = open_special; cmd '[class="special"] move scratchpad'; -my ($nodes, $focus) = get_ws_content($tmp); -is(scalar @$nodes, 1, 'one window on current ws'); +is_num_children($tmp, 1, 'one window on current ws'); my $otmp = fresh_workspace; cmd 'scratchpad show'; diff --git a/testcases/t/501-scratchpad.t b/testcases/t/501-scratchpad.t index 97a64a4a..9305239a 100644 --- a/testcases/t/501-scratchpad.t +++ b/testcases/t/501-scratchpad.t @@ -25,18 +25,18 @@ my $i3 = i3(get_socket_path()); sub verify_scratchpad_on_same_ws { my ($ws) = @_; - is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($ws, 0, 'no nodes on this ws'); my $window = open_window; - is(scalar @{get_ws($ws)->{nodes}}, 1, 'one nodes on this ws'); + is_num_children($ws, 1, 'one nodes on this ws'); cmd 'move scratchpad'; - is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($ws, 0, 'no nodes on this ws'); cmd 'scratchpad show'; - is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($ws, 0, 'no nodes on this ws'); is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on this ws'); } @@ -61,21 +61,21 @@ sub verify_scratchpad_switch { cmd "workspace $first"; - is(scalar @{get_ws($first)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($first, 0, 'no nodes on this ws'); my $window = open_window; - is(scalar @{get_ws($first)->{nodes}}, 1, 'one nodes on this ws'); + is_num_children($first, 1, 'one nodes on this ws'); cmd 'move scratchpad'; - is(scalar @{get_ws($first)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($first, 0, 'no nodes on this ws'); cmd "workspace $second"; cmd 'scratchpad show'; my $ws = get_ws($second); - is(scalar @{$ws->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($second, 0, 'no nodes on this ws'); is(scalar @{$ws->{floating_nodes}}, 1, 'one floating node on this ws'); # Verify that the coordinates are within bounds. diff --git a/testcases/t/505-scratchpad-resolution.t b/testcases/t/505-scratchpad-resolution.t index 3d0bcfa6..ce85cfc0 100644 --- a/testcases/t/505-scratchpad-resolution.t +++ b/testcases/t/505-scratchpad-resolution.t @@ -22,15 +22,13 @@ sync_with_i3; sub verify_scratchpad_doesnt_move { my ($ws) = @_; - is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($ws, 0, 'no nodes on this ws'); my $window = open_window; - - is(scalar @{get_ws($ws)->{nodes}}, 1, 'one nodes on this ws'); + is_num_children($ws, 1, 'one node on this ws'); cmd 'move scratchpad'; - - is(scalar @{get_ws($ws)->{nodes}}, 0, 'no nodes on this ws'); + is_num_children($ws, 0, 'no nodes on this ws'); my $last_x = -1; for (1 .. 20) { From 4976fa33504907510f1ee9e44884c116af0d801b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 00:22:38 +0200 Subject: [PATCH 161/200] con_set_layout: always use the parent container, handle workspaces properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, in case 'layout stacked' (for example) had been called interactively, con_set_layout would be called with focused->parent, while with for_window, it’d be called on the actual matching container. This difference in behavior was the cause for the inability to use 'for_window [class="XTerm"] layout tabbed', which now works \o/, but more on that below. The change also allows us to handle the case of the user selecting a CT_WORKSPACE container properly, that is, by using the special case and creating a new split container on the workspace which gets all the contents, but a new layout. Now, before you are enthusiastic about the change and try to use for_window magic in your config file, keep in mind: The 'layout' command acts on the parent split container. That is, when using a line such as this one: for_window [class="XTerm"] layout tabbed …and opening an XTerm when on a workspace with one single other window, the whole workspace will be set tabbed (just as previously when you opened an XTerm and sent 'layout tabbed' manually). Therefore, to open XTerm in its own tabbed split container, you need to split before: for_window [class="XTerm"] split v, layout tabbed The comma here is important! It says that the second command should not be treated as an entirely unrelated command, but it should also relate the matching window (while it does work with a ';', that is prone to race-conditions and should be avoided). fixes #358 --- src/commands.c | 14 +++++++++++--- src/con.c | 38 ++++++++++++++++++++++++++++---------- testcases/t/122-split.t | 26 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/commands.c b/src/commands.c index 00d8b97d..2d8fce3c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1114,9 +1114,17 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { * */ void cmd_split(I3_CMD, char *direction) { + owindow *current; /* TODO: use matches */ LOG("splitting in direction %c\n", direction[0]); - tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); + if (match_is_empty(current_match)) + tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); + else { + TAILQ_FOREACH(current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); + tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ)); + } + } cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -1429,7 +1437,7 @@ void cmd_layout(I3_CMD, char *layout_str) { /* check if the match is empty, not if the result is empty */ if (match_is_empty(current_match)) - con_set_layout(focused->parent, layout); + con_set_layout(focused, layout); else { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); @@ -1456,7 +1464,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) { /* check if the match is empty, not if the result is empty */ if (match_is_empty(current_match)) - con_toggle_layout(focused->parent, toggle_mode); + con_toggle_layout(focused, toggle_mode); else { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); diff --git a/src/con.c b/src/con.c index 0c82163a..cb756b6d 100644 --- a/src/con.c +++ b/src/con.c @@ -1082,6 +1082,15 @@ void con_set_border_style(Con *con, int border_style) { * */ void con_set_layout(Con *con, int layout) { + DLOG("con_set_layout(%p, %d), con->type = %d\n", + con, layout, con->type); + + /* Users can focus workspaces, but not any higher in the hierarchy. + * Focus on the workspace is a special case, since in every other case, the + * user means "change the layout of the parent split container". */ + if (con->type != CT_WORKSPACE) + con = con->parent; + /* We fill in last_split_layout when switching to a different layout * since there are many places in the code that don’t use * con_set_layout(). */ @@ -1092,7 +1101,8 @@ void con_set_layout(Con *con, int layout) { * whole workspace into stacked/tabbed mode. To do this and still allow * intuitive operations (like level-up and then opening a new window), we * need to create a new split container. */ - if (con->type == CT_WORKSPACE) { + if (con->type == CT_WORKSPACE && + (layout == L_STACKED || layout == L_TABBED)) { DLOG("Creating new split container\n"); /* 1: create a new split container */ Con *new = con_new(NULL, NULL); @@ -1100,7 +1110,7 @@ void con_set_layout(Con *con, int layout) { /* 2: Set the requested layout on the split container and mark it as * split. */ - con_set_layout(new, layout); + new->layout = layout; new->last_split_layout = con->last_split_layout; new->split = true; @@ -1156,30 +1166,38 @@ void con_set_layout(Con *con, int layout) { * */ void con_toggle_layout(Con *con, const char *toggle_mode) { + Con *parent = con; + /* Users can focus workspaces, but not any higher in the hierarchy. + * Focus on the workspace is a special case, since in every other case, the + * user means "change the layout of the parent split container". */ + if (con->type != CT_WORKSPACE) + parent = con->parent; + DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent); + if (strcmp(toggle_mode, "split") == 0) { /* Toggle between splits. When the current layout is not a split * layout, we just switch back to last_split_layout. Otherwise, we * change to the opposite split layout. */ - if (con->layout != L_SPLITH && con->layout != L_SPLITV) - con_set_layout(con, con->last_split_layout); + if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) + con_set_layout(con, parent->last_split_layout); else { - if (con->layout == L_SPLITH) + if (parent->layout == L_SPLITH) con_set_layout(con, L_SPLITV); else con_set_layout(con, L_SPLITH); } } else { - if (con->layout == L_STACKED) + if (parent->layout == L_STACKED) con_set_layout(con, L_TABBED); - else if (con->layout == L_TABBED) { + else if (parent->layout == L_TABBED) { if (strcmp(toggle_mode, "all") == 0) con_set_layout(con, L_SPLITH); - else con_set_layout(con, con->last_split_layout); - } else if (con->layout == L_SPLITH || con->layout == L_SPLITV) { + else con_set_layout(con, parent->last_split_layout); + } else if (parent->layout == L_SPLITH || parent->layout == L_SPLITV) { if (strcmp(toggle_mode, "all") == 0) { /* When toggling through all modes, we toggle between * splith/splitv, whereas normally we just directly jump to * stacked. */ - if (con->layout == L_SPLITH) + if (parent->layout == L_SPLITH) con_set_layout(con, L_SPLITV); else con_set_layout(con, L_STACKED); } else { diff --git a/testcases/t/122-split.t b/testcases/t/122-split.t index d491c37a..7f8f392d 100644 --- a/testcases/t/122-split.t +++ b/testcases/t/122-split.t @@ -4,6 +4,7 @@ # Tests splitting # use i3test; +use List::Util qw(first); my $tmp; my $ws; @@ -119,4 +120,29 @@ cmd 'open'; is(scalar @content, 1, 'Still one container on this ws'); is(scalar @{$content[0]->{nodes}}, 1, 'Stacked con still has one child node'); +################################################################################ +# When focusing the workspace, changing the layout should have an effect on the +# workspace, not on the parent (CT_CONTENT) container. +################################################################################ + +sub get_output_content { + my $tree = i3(get_socket_path())->get_tree->recv; + + my @outputs = grep { $_->{name} !~ /^__/ } @{$tree->{nodes}}; + is(scalar @outputs, 1, 'exactly one output (testcase not multi-monitor capable)'); + my $output = $outputs[0]; + # get the first (and only) CT_CON + return first { $_->{type} == 2 } @{$output->{nodes}}; +} + +$tmp = fresh_workspace; + +cmd 'open'; +cmd 'split v'; +cmd 'open'; +cmd 'focus parent'; +is(get_output_content()->{layout}, 'splith', 'content container layout ok'); +cmd 'layout stacked'; +is(get_output_content()->{layout}, 'splith', 'content container layout still ok'); + done_testing; From 32d4dbf70f38aa827f3d3ca33cd81d52f4fc4856 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 21:00:08 +0200 Subject: [PATCH 162/200] startup-notifications: keep sequence around for 30s after completion This changes the fact that Firefox would not be launched on the correct workspace because it marked the startup sequence as completed *before* actually mapping all of its windows. To test this, go to workspace 3 and run this command in a terminal: i3-msg 'exec iceweasel; workspace 4' That will make i3 start iceweasel (and create a proper startup notification context for it), then immediately switch to workspace 4 (before iceweasel could possibly start). The iceweasel window(s) should appear on workspace 3. --- include/data.h | 3 +++ src/startup.c | 66 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/include/data.h b/include/data.h index 6df3f6fc..d60ec3cd 100644 --- a/include/data.h +++ b/include/data.h @@ -168,6 +168,9 @@ struct Startup_Sequence { char *workspace; /** libstartup-notification context for this launch */ SnLauncherContext *context; + /** time at which this sequence should be deleted (after it was marked as + * completed) */ + time_t delete_at; TAILQ_ENTRY(Startup_Sequence) sequences; }; diff --git a/src/startup.c b/src/startup.c index b0aa2ca3..404927b4 100644 --- a/src/startup.c +++ b/src/startup.c @@ -57,6 +57,57 @@ static void startup_timeout(EV_P_ ev_timer *w, int revents) { free(w); } +/* + * Some applications (such as Firefox) mark a startup sequence as completede + * *before* they even map a window. Therefore, we cannot entirely delete the + * startup sequence once it’s marked as complete. Instead, we’ll mark it for + * deletion in 30 seconds and use that chance to delete old sequences. + * + * This function returns the number of active (!) startup notifications, that + * is, those which are not marked for deletion yet. This is used for changing + * the root window cursor. + * + */ +static int _delete_startup_sequence(struct Startup_Sequence *sequence) { + time_t current_time = time(NULL); + int active_sequences = 0; + + /* Mark the given sequence for deletion in 30 seconds. */ + sequence->delete_at = current_time + 30; + DLOG("Will delete startup sequence %s at timestamp %d\n", + sequence->id, sequence->delete_at); + + /* Traverse the list and delete everything which was marked for deletion 30 + * seconds ago or earlier. */ + struct Startup_Sequence *current, *next; + for (next = TAILQ_FIRST(&startup_sequences); + next != TAILQ_END(&startup_sequences); + ) { + current = next; + next = TAILQ_NEXT(next, sequences); + + if (current->delete_at == 0) { + active_sequences++; + continue; + } + + if (current_time <= current->delete_at) + continue; + + DLOG("Deleting startup sequence %s, delete_at = %d, current_time = %d\n", + current->id, current->delete_at, current_time); + + /* Unref the context, will be free()d */ + sn_launcher_context_unref(current->context); + + /* Delete our internal sequence */ + TAILQ_REMOVE(&startup_sequences, current, sequences); + } + + return active_sequences; + +} + /* * Starts the given application by passing it through a shell. We use double fork * to avoid zombie processes. As the started application’s parent exits (immediately), @@ -182,13 +233,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) { case SN_MONITOR_EVENT_COMPLETED: DLOG("startup sequence %s completed\n", sn_startup_sequence_get_id(snsequence)); - /* Unref the context, will be free()d */ - sn_launcher_context_unref(sequence->context); - - /* Delete our internal sequence */ - TAILQ_REMOVE(&startup_sequences, sequence, sequences); - - if (TAILQ_EMPTY(&startup_sequences)) { + if (_delete_startup_sequence(sequence) == 0) { DLOG("No more startup sequences running, changing root window cursor to default pointer.\n"); /* Change the pointer of the root window to indicate progress */ if (xcursor_supported) @@ -250,13 +295,14 @@ char *startup_workspace_for_window(i3Window *cwindow, xcb_get_property_reply_t * break; } - free(startup_id); - free(startup_id_reply); - if (!sequence) { DLOG("WARNING: This sequence (ID %s) was not found\n", startup_id); + free(startup_id); + free(startup_id_reply); return NULL; } + free(startup_id); + free(startup_id_reply); return sequence->workspace; } From a598544b5ad21f8be2cf4940235c7dd360fb9772 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 21:10:26 +0200 Subject: [PATCH 163/200] userguide: point out explicitly how to use startup-notifications --- docs/userguide | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/userguide b/docs/userguide index bc894800..e60b913c 100644 --- a/docs/userguide +++ b/docs/userguide @@ -604,6 +604,22 @@ logfile first (see http://i3wm.org/docs/debugging.html). It includes more details about the matching process and the window’s actual class, instance and title when starting up. +Note that if you want to start an application just once on a specific +workspace, but you don’t want to assign all instances of it permanently, you +can make use of i3’s startup-notification support (see <>) in your config +file in the following way: + +*Start iceweasel on workspace 3 (once)*: +------------------------------------------------------------------------------- +# Start iceweasel on workspace 3, then switch back to workspace 1 +# (Being a command-line utility, i3-msg does not support startup notifications, +# hence the exec --no-startup-id.) +# (Starting iceweasel with i3’s exec command is important in order to make i3 +# create a startup notification context, without which the iceweasel window(s) +# cannot be matched onto the workspace on which the command was started.) +exec --no-startup-id i3-msg 'workspace 3; exec iceweasel; workspace 1' +------------------------------------------------------------------------------- + === Automatically starting applications on i3 startup By using the +exec+ keyword outside a keybinding, you can configure From d644dbff640bb27971bcdbd58fe0ae503e8ab6e4 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 22:03:45 +0200 Subject: [PATCH 164/200] bugfix: forgot to mark split containers as split = true (+test) (Thanks szalik) When the workspace layout (formerly orientation) was forced to change due to a move command, the split container we created was not marked as split = true, which caused tree_flatten() to errnously kill the contents of it and thus one window ended up unmanaged. Also, the logic in tree_flatten() was inverted due to commit de94f6da. fixes #790 --- src/tree.c | 4 ++-- src/workspace.c | 4 +++- testcases/t/197-regression-move-vanish.t | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 testcases/t/197-regression-move-vanish.t diff --git a/src/tree.c b/src/tree.c index 2c1c257e..321bc78a 100644 --- a/src/tree.c +++ b/src/tree.c @@ -617,8 +617,8 @@ void tree_flatten(Con *con) { /* The child must have a different orientation than the con but the same as * the con’s parent to be redundant */ - if (con->split || - child->split || + if (!con->split || + !child->split || con_orientation(con) == con_orientation(child) || con_orientation(child) != con_orientation(parent)) goto recurse; diff --git a/src/workspace.c b/src/workspace.c index 85331181..1749959a 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -699,6 +699,7 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { /* 1: create a new split container */ Con *split = con_new(NULL, NULL); split->parent = ws; + split->split = true; /* 2: copy layout from workspace */ split->layout = ws->layout; @@ -715,9 +716,10 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { /* 4: switch workspace layout */ ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; + DLOG("split->layout = %d, ws->layout = %d\n", split->layout, ws->layout); /* 5: attach the new split container to the workspace */ - DLOG("Attaching new split to ws\n"); + DLOG("Attaching new split (%p) to ws (%p)\n", split, ws); con_attach(split, ws, false); /* 6: fix the percentages */ diff --git a/testcases/t/197-regression-move-vanish.t b/testcases/t/197-regression-move-vanish.t new file mode 100644 index 00000000..41c7b87e --- /dev/null +++ b/testcases/t/197-regression-move-vanish.t @@ -0,0 +1,21 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# Regression test: moving a window to the right out of a splitv container would +# make it vanish. +# Ticket: #790 +# Bug still in: 4.2-277-ga598544 +use i3test; + +my $ws = fresh_workspace; + +my $top = open_window; +cmd 'split v'; +my $bottom = open_window; + +is_num_children($ws, 2, 'two windows on workspace level'); + +cmd 'move right'; + +is_num_children($ws, 2, 'still two windows on workspace level'); + +done_testing; From 34391ae885911d4aff933f6434f71babd65fc474 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 22:22:48 +0200 Subject: [PATCH 165/200] fix format string warnings --- src/startup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/startup.c b/src/startup.c index 404927b4..89324dbd 100644 --- a/src/startup.c +++ b/src/startup.c @@ -74,7 +74,7 @@ static int _delete_startup_sequence(struct Startup_Sequence *sequence) { /* Mark the given sequence for deletion in 30 seconds. */ sequence->delete_at = current_time + 30; - DLOG("Will delete startup sequence %s at timestamp %d\n", + DLOG("Will delete startup sequence %s at timestamp %ld\n", sequence->id, sequence->delete_at); /* Traverse the list and delete everything which was marked for deletion 30 @@ -94,7 +94,7 @@ static int _delete_startup_sequence(struct Startup_Sequence *sequence) { if (current_time <= current->delete_at) continue; - DLOG("Deleting startup sequence %s, delete_at = %d, current_time = %d\n", + DLOG("Deleting startup sequence %s, delete_at = %ld, current_time = %ld\n", current->id, current->delete_at, current_time); /* Unref the context, will be free()d */ From 1e3e6997f4f6046f6c2b2850837b01bf89cb0229 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Sep 2012 23:54:56 +0200 Subject: [PATCH 166/200] rendering: ceil() instead of truncating for tabbed deco_rect width (Thanks szalik) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In tabbed mode, the available width (say 1280) is divided by the amount of child containers (say 3). Before this commit, we just truncated the result and would end up with 426 + 426 + 426 = 1278 pixels that we render to. Now we render a bit too much, but that’ll at least not give us graphics corruption on any side :). fixes #791 --- src/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render.c b/src/render.c index d7b8cd28..01628c35 100644 --- a/src/render.c +++ b/src/render.c @@ -345,7 +345,7 @@ void render_con(Con *con, bool render_fullscreen) { child->rect.width = rect.width; child->rect.height = rect.height; - child->deco_rect.width = child->rect.width / children; + child->deco_rect.width = ceil((float)child->rect.width / children); child->deco_rect.x = x - con->rect.x + i * child->deco_rect.width; child->deco_rect.y = y - con->rect.y; From ea842f96861ae9c411ebc5a9cb535c465c98fa77 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 09:47:50 +0200 Subject: [PATCH 167/200] update release-notes --- RELEASE-NOTES-4.3 | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES-4.3 b/RELEASE-NOTES-4.3 index e7153d5a..2b62042e 100644 --- a/RELEASE-NOTES-4.3 +++ b/RELEASE-NOTES-4.3 @@ -59,6 +59,18 @@ Another very important change is that we now support pango for rendering text. in. In case you are working on a very low-spec embedded device, it is easy enough to disable pango support, see common.mk. + +Also, the 'layout' command now always works on the parent split container. This + allows you to do things like this: + + for_window [class="XTerm"] layout tabbed + + When you now open XTerm on an empty workspace, the whole workspace will be + set to tabbed. In case you want to open XTerm in its own tabbed split + container, you need to split before: + + for_window [class="XTerm"] split v, layout tabbed + ┌────────────────────────────┐ │ Changes in v4.3 │ └────────────────────────────┘ @@ -106,6 +118,9 @@ Another very important change is that we now support pango for rendering text. • i3bar: be less strict about the {"version":1} JSON header • shm-logging: implement i3-dump-log -f (follow) • Implement pango support + • 'move workspace number n' will now create the workspace if it doesn’t exist + • Accept slashes in RandR output names + • Keep startup-notification sequences around for 30s after completion ┌────────────────────────────┐ │ Bugfixes │ @@ -134,6 +149,15 @@ Another very important change is that we now support pango for rendering text. • Fix moving scratchpad window • Cleanup zero-byte logfile on immediate exit (they are created by i3 --get-socketpath for example). + • Fix resizing floating windows by height + • Fix back_and_forth in 'workspace number' for named workspaces + • Grab server and process pending events before managing existing windows + (fixes problems with GIMP windows not being managed after an in-place + restart) + • Don’t allow ConfigureRequests while in fullscreen (fixes a compatibility + issue with gnome-terminal and xfce’s terminal) + • Fix flickering with 1pixel border tabbed layouts + • Use _exit() instead of exit() when i3 utility programs cannot be executed ┌────────────────────────────┐ │ Thanks! │ @@ -142,9 +166,9 @@ Another very important change is that we now support pango for rendering text. Thanks for testing, bugfixes, discussions and everything I forgot go out to: aksr, Axel Wagner, darkraven, David Coppa, eeemsi, Felicitus, Fernando Tarlá - Cardoso Lemos, Iakov Davydov, jh, Julius Plenz, Marcel Hellwig, Marcus, - Michael Stapelberg, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel - Löbl, Philipp Middendorf, prg, Quentin Glidic, somelauw, stfn, tucos, + Cardoso Lemos, Iakov Davydov, jh, Joel Stemmer, Julius Plenz, Marcel Hellwig, + Marcus, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel Löbl, Philipp + Middendorf, prg, Quentin Glidic, Sebastian Ullrich, somelauw, stfn, tucos, TunnelWicht, Valentin Haenel --- Michael Stapelberg, 2012-08-18 +-- Michael Stapelberg, 2012-09-06 From f44d4ce4b3467b6c4ab037d494fa1ae50ef9df07 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 14:49:14 +0200 Subject: [PATCH 168/200] re-indent src/config.c --- src/config.c | 124 +++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/config.c b/src/config.c index c5846279..017d40ce 100644 --- a/src/config.c +++ b/src/config.c @@ -26,27 +26,27 @@ struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs); * */ void ungrab_all_keys(xcb_connection_t *conn) { - DLOG("Ungrabbing all keys\n"); - xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY); + DLOG("Ungrabbing all keys\n"); + xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY); } static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) { - DLOG("Grabbing %d\n", keycode); - /* Grab the key in all combinations */ - #define GRAB_KEY(modifier) \ - do { \ - xcb_grab_key(conn, 0, root, modifier, keycode, \ - XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \ - } while (0) - int mods = bind->mods; - if ((bind->mods & BIND_MODE_SWITCH) != 0) { - mods &= ~BIND_MODE_SWITCH; - if (mods == 0) - mods = XCB_MOD_MASK_ANY; - } - GRAB_KEY(mods); - GRAB_KEY(mods | xcb_numlock_mask); - GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK); + DLOG("Grabbing %d\n", keycode); + /* Grab the key in all combinations */ + #define GRAB_KEY(modifier) \ + do { \ + xcb_grab_key(conn, 0, root, modifier, keycode, \ + XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \ + } while (0) + int mods = bind->mods; + if ((bind->mods & BIND_MODE_SWITCH) != 0) { + mods &= ~BIND_MODE_SWITCH; + if (mods == 0) + mods = XCB_MOD_MASK_ANY; + } + GRAB_KEY(mods); + GRAB_KEY(mods | xcb_numlock_mask); + GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK); } /* @@ -55,28 +55,28 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint * */ Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) { - Binding *bind; + Binding *bind; - TAILQ_FOREACH(bind, bindings, bindings) { - /* First compare the modifiers */ - if (bind->mods != modifiers) - continue; + TAILQ_FOREACH(bind, bindings, bindings) { + /* First compare the modifiers */ + if (bind->mods != modifiers) + continue; - /* If a symbol was specified by the user, we need to look in - * the array of translated keycodes for the event’s keycode */ - if (bind->symbol != NULL) { - if (memmem(bind->translated_to, - bind->number_keycodes * sizeof(xcb_keycode_t), - &keycode, sizeof(xcb_keycode_t)) != NULL) - break; - } else { - /* This case is easier: The user specified a keycode */ - if (bind->keycode == keycode) - break; - } + /* If a symbol was specified by the user, we need to look in + * the array of translated keycodes for the event’s keycode */ + if (bind->symbol != NULL) { + if (memmem(bind->translated_to, + bind->number_keycodes * sizeof(xcb_keycode_t), + &keycode, sizeof(xcb_keycode_t)) != NULL) + break; + } else { + /* This case is easier: The user specified a keycode */ + if (bind->keycode == keycode) + break; } + } - return (bind == TAILQ_END(bindings) ? NULL : bind); + return (bind == TAILQ_END(bindings) ? NULL : bind); } /* @@ -133,22 +133,22 @@ void translate_keysyms(void) { * */ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) { - Binding *bind; - TAILQ_FOREACH(bind, bindings, bindings) { - if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) || - (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0)) - continue; + Binding *bind; + TAILQ_FOREACH(bind, bindings, bindings) { + if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) || + (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0)) + continue; - /* The easy case: the user specified a keycode directly. */ - if (bind->keycode > 0) { - grab_keycode_for_binding(conn, bind, bind->keycode); - continue; - } - - xcb_keycode_t *walk = bind->translated_to; - for (int i = 0; i < bind->number_keycodes; i++) - grab_keycode_for_binding(conn, bind, *walk++); + /* The easy case: the user specified a keycode directly. */ + if (bind->keycode > 0) { + grab_keycode_for_binding(conn, bind, bind->keycode); + continue; } + + xcb_keycode_t *walk = bind->translated_to; + for (int i = 0; i < bind->number_keycodes; i++) + grab_keycode_for_binding(conn, bind, *walk++); + } } /* @@ -156,22 +156,22 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) { * */ void switch_mode(const char *new_mode) { - struct Mode *mode; + struct Mode *mode; - LOG("Switching to mode %s\n", new_mode); + LOG("Switching to mode %s\n", new_mode); - SLIST_FOREACH(mode, &modes, modes) { - if (strcasecmp(mode->name, new_mode) != 0) - continue; + SLIST_FOREACH(mode, &modes, modes) { + if (strcasecmp(mode->name, new_mode) != 0) + continue; - ungrab_all_keys(conn); - bindings = mode->bindings; - translate_keysyms(); - grab_all_keys(conn, false); - return; - } + ungrab_all_keys(conn); + bindings = mode->bindings; + translate_keysyms(); + grab_all_keys(conn, false); + return; + } - ELOG("ERROR: Mode not found\n"); + ELOG("ERROR: Mode not found\n"); } /* From c6c6d3a952a647781c070a6783b1335c4271325a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 17:04:31 +0200 Subject: [PATCH 169/200] naive implementation of 'bindsym --release' (and bindcode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation is naive because the user has to generate exactly the event he specified. That is, if you use this binding: bindsym --release $mod+x exec import /tmp/latest-screenshot.png Then it will only be triggered if you hit $mod, hit x, release x, release $mod. It will not be triggered if you hit $mod, hit x, release $mod, release x. The reason is that the KeyRelease event in the latter case will not have the modifier in its flags, so it doesn’t match the configured binding. --- include/config.h | 2 +- include/data.h | 4 ++++ src/cfgparse.l | 7 +++++-- src/cfgparse.y | 31 +++++++++++++++++++++---------- src/config.c | 6 +++++- src/handlers.c | 1 + src/key_press.c | 19 +++++++++++++------ 7 files changed, 50 insertions(+), 20 deletions(-) diff --git a/include/config.h b/include/config.h index ebb24864..1a48016a 100644 --- a/include/config.h +++ b/include/config.h @@ -312,7 +312,7 @@ void switch_mode(const char *new_mode); * or NULL if no such binding exists. * */ -Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode); +Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode); /** * Kills the configerror i3-nagbar process, if any. diff --git a/include/data.h b/include/data.h index d60ec3cd..4b886ae8 100644 --- a/include/data.h +++ b/include/data.h @@ -200,6 +200,10 @@ struct regex { * */ struct Binding { + /** If true, the binding should be executed upon a KeyRelease event, not a + * KeyPress (the default). */ + bool release; + /** Symbol the user specified in configfile, if any. This needs to be * stored with the binding to be able to re-convert it into a keycode * if the keyboard mapping changes (using Xmodmap for example) */ diff --git a/src/cfgparse.l b/src/cfgparse.l index 52cde189..8ee2a1da 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -66,6 +66,7 @@ EOL (\r?\n) %x BAR_COLOR %x EXEC +%x OPTRELEASE %% @@ -172,12 +173,14 @@ EOL (\r?\n) [ \t]+ { BEGIN(WANT_STRING); } --no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; } . { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); } +--release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; } +. { printf("anything else (optrelease): *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); } [0-9-]+ { yylval.number = atoi(yytext); return NUMBER; } bar { yy_push_state(BAR); return TOK_BAR; } mode { return TOKMODE; } bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } -bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } -bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; } +bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } +bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; } floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; } floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; } floating_modifier { return TOKFLOATING_MODIFIER; } diff --git a/src/cfgparse.y b/src/cfgparse.y index af7d77cf..5c166778 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -3,6 +3,8 @@ * vim:ts=4:sw=4:expandtab * */ +#undef I3__FILE__ +#define I3__FILE__ "cfgparse.y" #include #include #include @@ -782,6 +784,7 @@ void parse_file(const char *f) { %token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace" %token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace" %token TOK_NO_STARTUP_ID "--no-startup-id" +%token TOK_RELEASE "--release" %token TOK_MARK "mark" %token TOK_CLASS "class" @@ -811,6 +814,7 @@ void parse_file(const char *f) { %type bar_mode_mode %type bar_modifier_modifier %type optional_no_startup_id +%type optional_release %type command %type word_or_number %type qstring_or_number @@ -879,33 +883,40 @@ binding: ; bindcode: - binding_modifiers NUMBER command + optional_release binding_modifiers NUMBER command { - printf("\tFound keycode binding mod%d with key %d and command %s\n", $1, $2, $3); + DLOG("bindcode: release = %d, mod = %d, key = %d, command = %s\n", $1, $2, $3, $4); Binding *new = scalloc(sizeof(Binding)); - new->keycode = $2; - new->mods = $1; - new->command = $3; + new->release = $1; + new->keycode = $3; + new->mods = $2; + new->command = $4; $$ = new; } ; bindsym: - binding_modifiers word_or_number command + optional_release binding_modifiers word_or_number command { - printf("\tFound keysym binding mod%d with key %s and command %s\n", $1, $2, $3); + DLOG("bindsym: release = %d, mod = %d, key = %s, command = %s\n", $1, $2, $3, $4); Binding *new = scalloc(sizeof(Binding)); - new->symbol = $2; - new->mods = $1; - new->command = $3; + new->release = $1; + new->symbol = $3; + new->mods = $2; + new->command = $4; $$ = new; } ; +optional_release: + /* empty */ { $$ = false; } + | TOK_RELEASE { $$ = true; } + ; + for_window: TOK_FOR_WINDOW match command { diff --git a/src/config.c b/src/config.c index 017d40ce..41491d86 100644 --- a/src/config.c +++ b/src/config.c @@ -54,7 +54,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint * or NULL if no such binding exists. * */ -Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) { +Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) { Binding *bind; TAILQ_FOREACH(bind, bindings, bindings) { @@ -62,6 +62,10 @@ Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) { if (bind->mods != modifiers) continue; + /* Check if the binding is for a KeyPress or a KeyRelease event */ + if (bind->release != key_release) + continue; + /* If a symbol was specified by the user, we need to look in * the array of translated keycodes for the event’s keycode */ if (bind->symbol != NULL) { diff --git a/src/handlers.c b/src/handlers.c index 0d087e79..21a87342 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1045,6 +1045,7 @@ void handle_event(int type, xcb_generic_event_t *event) { switch (type) { case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: handle_key_press((xcb_key_press_event_t*)event); break; diff --git a/src/key_press.c b/src/key_press.c index 2a578d66..3f466a2e 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -227,15 +227,17 @@ static yajl_callbacks command_error_callbacks = { }; /* - * There was a key press. We compare this key code with our bindings table and pass - * the bound action to parse_command(). + * There was a KeyPress or KeyRelease (both events have the same fields). We + * compare this key code with our bindings table and pass the bound action to + * parse_command(). * */ void handle_key_press(xcb_key_press_event_t *event) { + bool key_release = (event->response_type == XCB_KEY_RELEASE); last_timestamp = event->time; - DLOG("Keypress %d, state raw = %d\n", event->detail, event->state); + DLOG("%s %d, state raw = %d\n", (key_release ? "KeyRelease" : "KeyPress"), event->detail, event->state); /* Remove the numlock bit, all other bits are modifiers we can bind to */ uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); @@ -251,7 +253,7 @@ void handle_key_press(xcb_key_press_event_t *event) { DLOG("(checked mode_switch, state %d)\n", state_filtered); /* Find the binding */ - Binding *bind = get_binding(state_filtered, event->detail); + Binding *bind = get_binding(state_filtered, key_release, event->detail); /* No match? Then the user has Mode_switch enabled but does not have a * specific keybinding. Fall back to the default keybindings (without @@ -260,8 +262,13 @@ void handle_key_press(xcb_key_press_event_t *event) { if (bind == NULL) { state_filtered &= ~(BIND_MODE_SWITCH); DLOG("no match, new state_filtered = %d\n", state_filtered); - if ((bind = get_binding(state_filtered, event->detail)) == NULL) { - ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n", + if ((bind = get_binding(state_filtered, key_release, event->detail)) == NULL) { + /* This is not a real error since we can have release and + * non-release keybindings. On a KeyPress event for which there is + * only a !release-binding, but no release-binding, the + * corresponding KeyRelease event will trigger this. No problem, + * though. */ + DLOG("Could not lookup key binding (modifiers %d, keycode %d)\n", state_filtered, event->detail); return; } From 548d74015c50d7fae14bfb8bb1989acde5fc22ae Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 17:24:30 +0200 Subject: [PATCH 170/200] ignore modifiers for KeyRelease bindings For the following binding: # Simulate ctrl+v upon pressing $mod+x bindsym --release $mod+x exec --no-startup-id xdotool key --clearmodifiers ctrl+v you can now use either: 1. press $mod, press x, release x, release $mod 2. press $mod, press x, release $mod, release x fixes #485 --- include/data.h | 12 +++++++++++- src/cfgparse.y | 4 ++-- src/config.c | 28 +++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/include/data.h b/include/data.h index 4b886ae8..e8df78c7 100644 --- a/include/data.h +++ b/include/data.h @@ -202,7 +202,17 @@ struct regex { struct Binding { /** If true, the binding should be executed upon a KeyRelease event, not a * KeyPress (the default). */ - bool release; + enum { + /* This binding will only be executed upon KeyPress events */ + B_UPON_KEYPRESS = 0, + /* This binding will be executed either upon a KeyRelease event, or… */ + B_UPON_KEYRELEASE = 1, + /* …upon a KeyRelease event, even if the modifiers don’t match. This + * state is triggered from get_binding() when the corresponding + * KeyPress (!) happens, so that users can release the modifier keys + * before releasing the actual key. */ + B_UPON_KEYRELEASE_IGNORE_MODS = 2, + } release; /** Symbol the user specified in configfile, if any. This needs to be * stored with the binding to be able to re-convert it into a keycode diff --git a/src/cfgparse.y b/src/cfgparse.y index 5c166778..f175c720 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -913,8 +913,8 @@ bindsym: ; optional_release: - /* empty */ { $$ = false; } - | TOK_RELEASE { $$ = true; } + /* empty */ { $$ = B_UPON_KEYPRESS; } + | TOK_RELEASE { $$ = B_UPON_KEYRELEASE; } ; for_window: diff --git a/src/config.c b/src/config.c index 41491d86..fcf3841e 100644 --- a/src/config.c +++ b/src/config.c @@ -57,13 +57,35 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) { Binding *bind; + if (!key_release) { + /* On a KeyPress event, we first reset all + * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */ + TAILQ_FOREACH(bind, bindings, bindings) { + if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS) + bind->release = B_UPON_KEYRELEASE; + } + + /* Then we transition the KeyRelease bindings into a state where the + * modifiers no longer matter for the KeyRelease event so that users + * can release the modifier key before releasing the actual key. */ + TAILQ_FOREACH(bind, bindings, bindings) { + if (bind->release == B_UPON_KEYRELEASE && !key_release) + bind->release = B_UPON_KEYRELEASE_IGNORE_MODS; + } + } + TAILQ_FOREACH(bind, bindings, bindings) { - /* First compare the modifiers */ - if (bind->mods != modifiers) + /* First compare the modifiers (unless this is a + * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease + * event) */ + if (bind->mods != modifiers && + (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS || + !key_release)) continue; /* Check if the binding is for a KeyPress or a KeyRelease event */ - if (bind->release != key_release) + if ((bind->release == B_UPON_KEYPRESS && key_release) || + (bind->release >= B_UPON_KEYRELEASE && !key_release)) continue; /* If a symbol was specified by the user, we need to look in From b15dd83b26238f671de0772edf4f80a91c90e3b8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 17:31:16 +0200 Subject: [PATCH 171/200] docs/userguide: use --no-startup-id for executing shell scripts --- docs/userguide | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide b/docs/userguide index e60b913c..0a9458cf 100644 --- a/docs/userguide +++ b/docs/userguide @@ -363,7 +363,7 @@ bindsym mod+f fullscreen bindsym mod+Shift+r restart # Notebook-specific hotkeys -bindcode 214 exec /home/michael/toggle_beamer.sh +bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh -------------------------------- Available Modifiers: From 41868273860613ffbbe5d2e517131886d884617b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 17:31:30 +0200 Subject: [PATCH 172/200] docs/userguide: document the --release flag for key bindings --- docs/userguide | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/userguide b/docs/userguide index 0a9458cf..e42a6acb 100644 --- a/docs/userguide +++ b/docs/userguide @@ -348,10 +348,15 @@ your bindings in the same physical location on the keyboard, use keycodes. If you don’t switch layouts, and want a clean and simple config file, use keysyms. +Some tools (such as +import+ or +xdotool+) might be unable to run upon a +KeyPress event, because the keyboard/pointer is still grabbed. For these +situations, the +--release+ flag can be used, which will execute the command +after the keys have been released. + *Syntax*: ---------------------------------- -bindsym [Modifiers+]keysym command -bindcode [Modifiers+]keycode command +bindsym [--release] [Modifiers+]keysym command +bindcode [--release] [Modifiers+]keycode command ---------------------------------- *Examples*: @@ -364,6 +369,12 @@ bindsym mod+Shift+r restart # Notebook-specific hotkeys bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh + +# Simulate ctrl+v upon pressing $mod+x +bindsym --release $mod+x exec --no-startup-id xdotool key --clearmodifiers ctrl+v + +# Take a screenshot upon pressing $mod+x (select an area) +bindsym --release $mod+x exec --no-startup-id import /tmp/latest-screenshot.png -------------------------------- Available Modifiers: From a1823e59fffb9b25fe3cbb86d025203feb8f7d79 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Sep 2012 18:11:16 +0200 Subject: [PATCH 173/200] =?UTF-8?q?don=E2=80=99t=20errnously=20detect=20--?= =?UTF-8?q?release=20bindings=20as=20duplicates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cfgparse.y | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cfgparse.y b/src/cfgparse.y index f175c720..29c519f0 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -431,8 +431,10 @@ static void check_for_duplicate_bindings(struct context *context) { /* Check if the keycodes or modifiers are different. If so, they * can't be duplicate */ if (bind->keycode != current->keycode || - bind->mods != current->mods) + bind->mods != current->mods || + bind->release != current->release) continue; + context->has_errors = true; if (current->keycode != 0) { ELOG("Duplicate keybinding in config file:\n modmask %d with keycode %d, command \"%s\"\n", From 02ce246caf3ad62687f7cbf2ef1f4fe64dee33a0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 9 Sep 2012 15:30:37 +0200 Subject: [PATCH 174/200] build fix: remove src/cmdparse.* in make clean This was removed in commit 8853334bbefd57f8f9bd6bdeb0c221616b40b446 --- src/i3.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i3.mk b/src/i3.mk index b5c7c477..a0985120 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -82,4 +82,4 @@ install-i3: i3 clean-i3: echo "[i3] Clean" - rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} + rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} src/cmdparse.* From b4afd20d213ab7d08c6682801af736f58b8d1843 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 9 Sep 2012 15:39:49 +0200 Subject: [PATCH 175/200] use pwd -P instead of readlink -f (Thanks Marcus Crestani) readlink -f does not work on Mac OS X --- src/i3.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i3.mk b/src/i3.mk index a0985120..a93cc4c4 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -21,13 +21,13 @@ endif i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) -# The basename/readlink calls are for canonicalizing the path: Instead +# The basename/pwd calls are for canonicalizing the path: Instead # of src/main.c, we will see something like ../i3-4.2/src/main.c in # debugger backtraces, making it clearer which code belongs to i3 and # which code doesn’t. # We only do this for src/ since all the other subdirectories contain i3 in # their name already. -canonical_path := ../$(shell basename $(shell readlink -f .)) +canonical_path := ../$(shell basename $(shell pwd -P)) include/all.h.pch: $(i3_HEADERS) echo "[i3] PCH all.h" From 5bea7cb7df2f1ca41c6b803a44f7c3a1e5a2ea39 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 12:03:14 +0200 Subject: [PATCH 176/200] docs: generate HTML from testsuite POD documentation --- DEPENDS | 3 ++ common.mk | 1 + debian/control | 3 +- docs/docs.mk | 15 ++++++-- docs/i3-pod2html | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100755 docs/i3-pod2html diff --git a/DEPENDS b/DEPENDS index 35a03f25..fe9ba17f 100644 --- a/DEPENDS +++ b/DEPENDS @@ -16,6 +16,7 @@ │ yajl │ 1.0.8 │ 2.0.1 │ http://lloyd.github.com/yajl/ │ │ asciidoc │ 8.3.0 │ 8.6.4 │ 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/ │ │ libxcursor │ 1.1.11 │ 1.1.11 │ http://ftp.x.org/pub/current/src/lib/ │ │ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │ @@ -25,6 +26,8 @@ │ cairo │ 1.12.2 │ 1.12.2 │ http://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 i3bar, i3-msg, i3-input, i3-nagbar and i3-config-wizard do not introduce any new dependencies. diff --git a/common.mk b/common.mk index 8de425d0..bb5cf793 100644 --- a/common.mk +++ b/common.mk @@ -194,6 +194,7 @@ ifeq ($(V),0) # echo-ing vars V_ASCIIDOC = echo ASCIIDOC $@; +V_POD2HTML = echo POD2HTML $@; V_A2X = echo A2X $@; endif diff --git a/debian/control b/debian/control index 4ec5cbc8..02f00de2 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,8 @@ Build-Depends: debhelper (>= 7.0.50~), libpcre3-dev, libstartup-notification0-dev (>= 0.10), libcairo2-dev, - libpango1.0-dev + libpango1.0-dev, + libpod-simple-perl Standards-Version: 3.9.3 Homepage: http://i3wm.org/ diff --git a/docs/docs.mk b/docs/docs.mk index 773c8322..c0daed64 100644 --- a/docs/docs.mk +++ b/docs/docs.mk @@ -2,6 +2,7 @@ DISTCLEAN_TARGETS += clean-docs # To pass additional parameters for asciidoc ASCIIDOC = asciidoc +I3POD2HTML = ./docs/i3-pod2html ASCIIDOC_NOTOC_TARGETS = \ docs/debugging.html \ @@ -23,7 +24,17 @@ ASCIIDOC_TARGETS = \ ASCIIDOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -n $(ASCIIDOC_FLAGS) -o $@ $< ASCIIDOC_TOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -a toc -n $(ASCIIDOC_FLAGS) -o $@ $< -docs: $(ASCIIDOC_TARGETS) +POD2HTML_TARGETS = \ + docs/lib-i3test.html \ + docs/lib-i3test-test.html + +docs/lib-i3test.html: testcases/lib/i3test.pm + $(V_POD2HTML)$(I3POD2HTML) $< $@ + +docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm + $(V_POD2HTML)$(I3POD2HTML) $< $@ + +docs: $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS) $(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/% $(ASCIIDOC_TOC_CALL) @@ -32,4 +43,4 @@ $(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/% $(ASCIIDOC_CALL) clean-docs: - rm -f $(ASCIIDOC_TARGETS) + rm -f $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS) diff --git a/docs/i3-pod2html b/docs/i3-pod2html new file mode 100755 index 00000000..56a769f8 --- /dev/null +++ b/docs/i3-pod2html @@ -0,0 +1,90 @@ +#!/usr/bin/env perl +# vim:ts=4:sw=4:expandtab + +use strict; +use warnings; +use Pod::Simple::HTML; +use v5.10; + +$Pod::Simple::HTML::Tagmap{'Verbatim'} = '
';
+$Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '
';
+$Pod::Simple::HTML::Tagmap{'/Verbatim'} = '
'; +$Pod::Simple::HTML::Tagmap{'/VerbatimFormatted'} = '
'; + +if (@ARGV < 2) { + say STDERR "Syntax: i3-pod2html "; + exit 1; +} + +open(my $in, '<', $ARGV[0]) or die "Couldn’t open $ARGV[0] for reading: $!\n"; +open(my $out, '>', $ARGV[1]) or die "Couldn’t open $ARGV[1] for writing: $!\n"; + +my $parser = Pod::Simple::HTML->new(); + +$parser->index(1); +$parser->html_header_before_title( +<<'EOF' + + + + + + + + + + +EOF +); +$parser->html_header_after_title( +<<'EOF' + + + + +
+

i3 - improved tiling WM

+ +
+
+

i3 Perl documentation (testsuite)

+ +EOF +); + +$parser->output_fh($out); +$parser->parse_file($in); From 6bfbec9da3db23c5a9864ae51598845e18716705 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 13:23:49 +0200 Subject: [PATCH 177/200] tests: make cmp_float a real test instruction --- testcases/lib/i3test.pm | 7 ------ testcases/lib/i3test/Test.pm | 31 ++++++++++++++++++++++- testcases/t/141-resize.t | 44 ++++++++++++++++----------------- testcases/t/191-resize-levels.t | 4 +-- 4 files changed, 54 insertions(+), 32 deletions(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index 52deebc4..86886de1 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -33,7 +33,6 @@ our @EXPORT = qw( open_floating_window get_dock_clients cmd - cmp_float sync_with_i3 does_i3_live exit_gracefully @@ -566,13 +565,7 @@ sub launch_with_config { return $i3_pid; } -# compares two floats and return true if they differ less -# then 1e-6 -sub cmp_float { - my ($a, $b) = @_; - return abs($a - $b) < 1e-6; -} package i3test::X11; use parent 'X11::XCB::Connection'; diff --git a/testcases/lib/i3test/Test.pm b/testcases/lib/i3test/Test.pm index d83de55b..4389971b 100644 --- a/testcases/lib/i3test/Test.pm +++ b/testcases/lib/i3test/Test.pm @@ -1,8 +1,12 @@ package i3test::Test; +# vim:ts=4:sw=4:expandtab use base 'Test::Builder::Module'; -our @EXPORT = qw(is_num_children); +our @EXPORT = qw( + is_num_children + cmp_float +); my $CLASS = __PACKAGE__; @@ -54,6 +58,31 @@ sub is_num_children { $tb->is_num($got_num_children, $num_children, $name); } +=head2 cmp_float($a, $b) + +Compares floating point numbers C<$a> and C<$b> and returns true if they differ +less then 1e-6. + + $tmp = fresh_workspace; + + open_window for (1..4); + + cmd 'resize grow width 10 px or 25 ppt'; + + ($nodes, $focus) = get_ws_content($tmp); + ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%'); + ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%'); + ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%'); + ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%'); + +=cut +sub cmp_float { + my ($a, $b, $name) = @_; + my $tb = $CLASS->builder; + + $tb->cmp_ok(abs($a - $b), '<', 1e-6, $name); +} + =head1 AUTHOR Michael Stapelberg diff --git a/testcases/t/141-resize.t b/testcases/t/141-resize.t index 20dc68f4..e769ced7 100644 --- a/testcases/t/141-resize.t +++ b/testcases/t/141-resize.t @@ -22,8 +22,8 @@ cmd 'resize grow up 10 px or 25 ppt'; my ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); ############################################################ @@ -34,8 +34,8 @@ cmd 'split h'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); ############################################################ # checks that resizing within stacked/tabbed cons works @@ -52,14 +52,14 @@ cmd 'split h'; cmd 'layout stacked'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.5), 'top window got 50%'); -ok(cmp_float($nodes->[1]->{percent}, 0.5), 'bottom window got 50%'); +cmp_float($nodes->[0]->{percent}, 0.5, 'top window got 50%'); +cmp_float($nodes->[1]->{percent}, 0.5, 'bottom window got 50%'); cmd 'resize grow up 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'top window got 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%'); ############################################################ # Checks that resizing in the parent's parent's orientation works. @@ -79,14 +79,14 @@ $top = open_window; $bottom = open_window; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.5), 'left window got 50%'); -ok(cmp_float($nodes->[1]->{percent}, 0.5), 'right window got 50%'); +cmp_float($nodes->[0]->{percent}, 0.5, 'left window got 50%'); +cmp_float($nodes->[1]->{percent}, 0.5, 'right window got 50%'); cmd 'resize grow left 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'left window got 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%'); ################################################################################ # Check that the resize grow/shrink width/height syntax works. @@ -101,8 +101,8 @@ $right = open_window; cmd 'resize grow width 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'left window got 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%'); # Now test it with four windows $tmp = fresh_workspace; @@ -112,19 +112,19 @@ open_window for (1..4); cmd 'resize grow width 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%'); -ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%'); -ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%'); -ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%'); +cmp_float($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); +cmp_float($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); +cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); +cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); # height should be a no-op in this situation cmd 'resize grow height 10 px or 25 ppt'; ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%'); -ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%'); -ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%'); -ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%'); +cmp_float($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); +cmp_float($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); +cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); +cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); ############################################################ diff --git a/testcases/t/191-resize-levels.t b/testcases/t/191-resize-levels.t index 230a4915..9eef6c0c 100644 --- a/testcases/t/191-resize-levels.t +++ b/testcases/t/191-resize-levels.t @@ -23,7 +23,7 @@ cmd 'resize grow left 10px or 25ppt'; my ($nodes, $focus) = get_ws_content($tmp); -ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left container got only 25%'); -ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right container got 75%'); +cmp_float($nodes->[0]->{percent}, 0.25, 'left container got only 25%'); +cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%'); done_testing; From 0626c637aad5eba82ff44eb25ba45f8a96ed82ad Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 13:29:50 +0200 Subject: [PATCH 178/200] tests: move does_i3_live to i3test::Test --- testcases/lib/i3test.pm | 8 -------- testcases/lib/i3test/Test.pm | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index 86886de1..fe3b50c6 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -34,7 +34,6 @@ our @EXPORT = qw( get_dock_clients cmd sync_with_i3 - does_i3_live exit_gracefully workspace_exists focused_ws @@ -459,13 +458,6 @@ sub sync_with_i3 { }; } -sub does_i3_live { - my $tree = i3(get_socket_path())->get_tree->recv; - my @nodes = @{$tree->{nodes}}; - my $ok = (@nodes > 0); - $tester->ok($ok, 'i3 still lives'); - return $ok; -} # Tries to exit i3 gracefully (with the 'exit' cmd) or kills the PID if that fails sub exit_gracefully { diff --git a/testcases/lib/i3test/Test.pm b/testcases/lib/i3test/Test.pm index 4389971b..0253bc2d 100644 --- a/testcases/lib/i3test/Test.pm +++ b/testcases/lib/i3test/Test.pm @@ -6,6 +6,7 @@ use base 'Test::Builder::Module'; our @EXPORT = qw( is_num_children cmp_float + does_i3_live ); my $CLASS = __PACKAGE__; @@ -83,6 +84,22 @@ sub cmp_float { $tb->cmp_ok(abs($a - $b), '<', 1e-6, $name); } +=head2 does_i3_live + +Returns true if the layout tree can still be received from i3. + + # i3 used to crash on invalid commands in revision X + cmd 'invalid command'; + does_i3_live; + +=cut +sub does_i3_live { + my $tree = i3test::i3(i3test::get_socket_path())->get_tree->recv; + my @nodes = @{$tree->{nodes}}; + my $tb = $CLASS->builder; + $tb->ok((@nodes > 0), 'i3 still lives'); +} + =head1 AUTHOR Michael Stapelberg From 71607ccdd34508810de95f867e62335d1c08ef16 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 13:30:47 +0200 Subject: [PATCH 179/200] tests: document each and every i3test function --- testcases/lib/i3test.pm | 410 ++++++++++++++++++++++++++++++++++------ 1 file changed, 354 insertions(+), 56 deletions(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index fe3b50c6..2d38c775 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -45,6 +45,39 @@ our @EXPORT = qw( $x ); +=head1 NAME + +i3test - Testcase setup module + +=encoding utf-8 + +=head1 SYNOPSIS + + use i3test; + + my $ws = fresh_workspace; + is_num_children($ws, 0, 'no containers on this workspace yet'); + cmd 'open'; + is_num_children($ws, 1, 'one container after "open"'); + + done_testing; + +=head1 DESCRIPTION + +This module is used in every i3 testcase and takes care of automatically +starting i3 before any test instructions run. It also saves you typing of lots +of boilerplate in every test file. + + +i3test automatically "use"s C, C, C, +C’s C and C so that all of them are available +to you in your testcase. + +See also C (L) +which provides additional test instructions (like C or C). + +=cut + my $tester = Test::Builder->new(); my $_cached_socket_path = undef; my $_sync_window = undef; @@ -128,15 +161,19 @@ __ goto \&Exporter::import; } -# -# Waits for the next event and calls the given callback for every event to -# determine if this is the event we are waiting for. -# -# Can be used to wait until a window is mapped, until a ClientMessage is -# received, etc. -# -# wait_for_event $x, 0.25, sub { $_[0]->{response_type} == MAP_NOTIFY }; -# +=head1 EXPORT + +=head2 wait_for_event($timeout, $callback) + +Waits for the next event and calls the given callback for every event to +determine if this is the event we are waiting for. + +Can be used to wait until a window is mapped, until a ClientMessage is +received, etc. + + wait_for_event 0.25, sub { $_[0]->{response_type} == MAP_NOTIFY }; + +=cut sub wait_for_event { my ($timeout, $cb) = @_; @@ -165,8 +202,24 @@ sub wait_for_event { return $result; } -# thin wrapper around wait_for_event which waits for MAP_NOTIFY -# make sure to include 'structure_notify' in the window’s event_mask attribute +=head2 wait_for_map($window) + +Thin wrapper around wait_for_event which waits for MAP_NOTIFY. +Make sure to include 'structure_notify' in the window’s event_mask attribute. + +This function is called by C, so in most cases, you don’t need to +call it on your own. If you need special setup of the window before mapping, +you might have to map it on your own and use this function: + + my $window = open_window(dont_map => 1); + # Do something special with the window first + # … + + # Now map it and wait until it’s been mapped + $window->map; + wait_for_map($window); + +=cut sub wait_for_map { my ($win) = @_; my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win; @@ -175,9 +228,20 @@ sub wait_for_map { }; } -# Wrapper around wait_for_event which waits for UNMAP_NOTIFY. Also calls -# sync_with_i3 to make sure i3 also picked up and processed the UnmapNotify -# event. +=head2 wait_for_unmap($window) + +Wrapper around C which waits for UNMAP_NOTIFY. Also calls +C to make sure i3 also picked up and processed the UnmapNotify +event. + + my $ws = fresh_workspace; + my $window = open_window; + is_num_children($ws, 1, 'one window on workspace'); + $window->unmap; + wait_for_unmap; + is_num_children($ws, 0, 'no more windows on this workspace'); + +=cut sub wait_for_unmap { my ($win) = @_; # my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win; @@ -187,25 +251,71 @@ sub wait_for_unmap { sync_with_i3(); } -# -# Opens a new window (see X11::XCB::Window), maps it, waits until it got mapped -# and synchronizes with i3. -# -# set dont_map to a true value to avoid mapping -# -# if you want to change aspects of your window before it would be mapped, -# set before_map to a coderef. $window gets passed as $_ and as first argument. -# -# if you set both dont_map and before_map, the coderef will be called nevertheless -# -# -# default values: -# class => WINDOW_CLASS_INPUT_OUTPUT -# rect => [ 0, 0, 30, 30 ] -# background_color => '#c0c0c0' -# event_mask => [ 'structure_notify' ] -# name => 'Window ' -# +=head2 open_window([ $args ]) + +Opens a new window (see C), maps it, waits until it got mapped +and synchronizes with i3. + +The following arguments can be passed: + +=over 4 + +=item class + +The X11 window class (e.g. WINDOW_CLASS_INPUT_OUTPUT), not to be confused with +the WM_CLASS! + +=item rect + +An arrayref with 4 members specifying the initial geometry (position and size) +of the window, e.g. C<< [ 0, 100, 70, 50 ] >> for a window appearing at x=0, y=100 +with width=70 and height=50. + +Note that this is entirely irrelevant for tiling windows. + +=item background_color + +The background pixel color of the window, formatted as "#rrggbb", like HTML +color codes (e.g. #c0c0c0). This is useful to tell windows apart when actually +watching the testcases. + +=item event_mask + +An arrayref contining strings which describe the X11 event mask we use for that +window. The default is C<< [ 'structure_notify' ] >>. + +=item name + +The window’s C<_NET_WM_NAME> (UTF-8 window title). By default, this is "Window +n" with n being replaced by a counter to keep windows apart. + +=item dont_map + +Set to a true value to avoid mapping the window (making it visible). + +=item before_map + +A coderef which is called before the window is mapped (unless C is +true). The freshly created C<$window> is passed as C<$_> and as the first +argument. + +=back + +The default values are equivalent to this call: + + open_window( + class => WINDOW_CLASS_INPUT_OUTPUT + rect => [ 0, 0, 30, 30 ] + background_color => '#c0c0c0' + event_mask => [ 'structure_notify' ] + name => 'Window ' + ); + +Usually, though, calls are simpler: + + my $top_window = open_window; + +=cut sub open_window { my %args = @_ == 1 ? %{$_[0]} : @_; @@ -233,8 +343,14 @@ sub open_window { return $window; } -# Thin wrapper around open_window which sets window_type to -# _NET_WM_WINDOW_TYPE_UTILITY to make the window floating. +=head2 open_floating_window([ $args ]) + +Thin wrapper around open_window which sets window_type to +C<_NET_WM_WINDOW_TYPE_UTILITY> to make the window floating. + +The arguments are the same as those of C. + +=cut sub open_floating_window { my %args = @_ == 1 ? %{$_[0]} : @_; @@ -250,6 +366,15 @@ sub open_empty_con { return $reply->[0]->{id}; } +=head2 get_workspace_names() + +Returns an arrayref containing the name of every workspace (regardless of its +output) which currently exists. + + my $workspace_names = get_workspace_names; + is(scalar @$workspace_names, 3, 'three workspaces exist currently'); + +=cut sub get_workspace_names { my $i3 = i3(get_socket_path()); my $tree = $i3->get_tree->recv; @@ -264,6 +389,15 @@ sub get_workspace_names { [ map { $_->{name} } @cons ] } +=head2 get_unused_workspace + +Returns a workspace name which has not yet been used. See also +C which directly switches to an unused workspace. + + my $ws = get_unused_workspace; + cmd "workspace $ws"; + +=cut sub get_unused_workspace { my @names = get_workspace_names(); my $tmp; @@ -271,7 +405,7 @@ sub get_unused_workspace { $tmp } -=head2 fresh_workspace(...) +=head2 fresh_workspace([ $args ]) Switches to an unused workspace and returns the name of that workspace. @@ -304,6 +438,30 @@ sub fresh_workspace { $unused } +=head2 get_ws($workspace) + +Returns the container (from the i3 layout tree) which represents C<$workspace>. + + my $ws = fresh_workspace; + my $ws_con = get_ws($ws); + ok(!$ws_con->{urgent}, 'fresh workspace not marked urgent'); + +Here is an example which counts the number of urgent containers recursively, +starting from the workspace container: + + sub count_urgent { + my ($con) = @_; + + my @children = (@{$con->{nodes}}, @{$con->{floating_nodes}}); + my $urgent = grep { $_->{urgent} } @children; + $urgent += count_urgent($_) for @children; + return $urgent; + } + my $urgent = count_urgent(get_ws($ws)); + is($urgent, 3, "three urgent windows on workspace $ws"); + + +=cut sub get_ws { my ($name) = @_; my $i3 = i3(get_socket_path()); @@ -322,17 +480,61 @@ sub get_ws { return first { $_->{name} eq $name } @workspaces; } -# -# returns the content (== tree, starting from the node of a workspace) -# of a workspace. If called in array context, also includes the focus -# stack of the workspace -# +=head2 get_ws_content($workspace) + +Returns the content (== tree, starting from the node of a workspace) +of a workspace. If called in array context, also includes the focus +stack of the workspace. + + my $nodes = get_ws_content($ws); + is(scalar @$nodes, 4, 'there are four containers at workspace-level'); + +Or, in array context: + + my $window = open_window; + my ($nodes, $focus) = get_ws_content($ws); + is($focus->[0], $window->id, 'newly opened window focused'); + +Note that this function does not do recursion for you! It only returns the +containers B. If you want to work with all containers (even +nested ones) on a workspace, you have to use recursion: + + # NB: This function does not count floating windows + sub count_urgent { + my ($nodes) = @_; + + my $urgent = 0; + for my $con (@$nodes) { + $urgent++ if $con->{urgent}; + $urgent += count_urgent($con->{nodes}); + } + + return $urgent; + } + my $nodes = get_ws_content($ws); + my $urgent = count_urgent($nodes); + is($urgent, 3, "three urgent windows on workspace $ws"); + +If you also want to deal with floating windows, you have to use C +instead and access C<< ->{nodes} >> and C<< ->{floating_nodes} >> on your own. + +=cut sub get_ws_content { my ($name) = @_; my $con = get_ws($name); return wantarray ? ($con->{nodes}, $con->{focus}) : $con->{nodes}; } +=head2 get_focused($workspace) + +Returns the container ID of the currently focused container on C<$workspace>. + + my $ws = fresh_workspace; + my $first_window = open_window; + my $second_window = open_window; + is(get_focused($ws), $second_window, 'second window focused'); + +=cut sub get_focused { my ($ws) = @_; my $con = get_ws($ws); @@ -350,6 +552,16 @@ sub get_focused { return $lf; } +=head2 get_dock_clients([ $dockarea ]) + +Returns an array of all dock containers in C<$dockarea> (one of "top" or +"bottom"). If C<$dockarea> is not specified, returns an array of all dock +containers in any dockarea. + + my @docked = get_dock_clients; + is(scalar @docked, 0, 'no dock clients yet'); + +=cut sub get_dock_clients { my $which = shift; @@ -374,10 +586,30 @@ sub get_dock_clients { return @docked; } +=head2 cmd($command) + +Sends the specified command to i3. + + my $ws = unused_workspace; + cmd "workspace $ws"; + cmd 'focus right'; + +=cut sub cmd { i3(get_socket_path())->command(@_)->recv } +=head2 workspace_exists($workspace) + +Returns true if C<$workspace> is the name of an existing workspace. + + my $old_ws = focused_ws; + # switch away from where we currently are + fresh_workspace; + + ok(workspace_exists($old_ws), 'old workspace still exists'); + +=cut sub workspace_exists { my ($name) = @_; ($name ~~ @{get_workspace_names()}) @@ -387,6 +619,9 @@ sub workspace_exists { Returns the name of the currently focused workspace. + my $ws = focused_ws; + is($ws, '1', 'i3 starts on workspace 1'); + =cut sub focused_ws { my $i3 = i3(get_socket_path()); @@ -398,16 +633,31 @@ sub focused_ws { return $first->{name} } -# -# Sends an I3_SYNC ClientMessage with a random value to the root window. -# i3 will reply with the same value, but, due to the order of events it -# processes, only after all other events are done. -# -# This can be used to ensure the results of a cmd 'focus left' are pushed to -# X11 and that $x->input_focus returns the correct value afterwards. -# -# See also docs/testsuite for a long explanation -# +=head2 sync_with_i3([ $args ]) + +Sends an I3_SYNC ClientMessage with a random value to the root window. +i3 will reply with the same value, but, due to the order of events it +processes, only after all other events are done. + +This can be used to ensure the results of a cmd 'focus left' are pushed to +X11 and that C<< $x->input_focus >> returns the correct value afterwards. + +See also L for a longer explanation. + + my $window = open_window; + $window->add_hint('urgency'); + # Ensure i3 picked up the change + sync_with_i3; + +The only time when you need to use the C argument is when you just +killed your own X11 connection: + + cmd 'kill client'; + # We need to re-establish the X11 connection which we just killed :). + $x = i3test::X11->new; + sync_with_i3(no_cache => 1); + +=cut sub sync_with_i3 { my %args = @_ == 1 ? %{$_[0]} : @_; @@ -458,8 +708,22 @@ sub sync_with_i3 { }; } +=head2 exit_gracefully($pid, [ $socketpath ]) -# Tries to exit i3 gracefully (with the 'exit' cmd) or kills the PID if that fails +Tries to exit i3 gracefully (with the 'exit' cmd) or kills the PID if that fails. + +If C<$socketpath> is not specified, C will be called. + +You only need to use this function if you have launched i3 on your own with +C. Otherwise, it will be automatically called when the +testcase ends. + + use i3test i3_autostart => 0; + my $pid = launch_with_config($config); + # … + exit_gracefully($pid); + +=cut sub exit_gracefully { my ($pid, $socketpath) = @_; $socketpath ||= get_socket_path(); @@ -484,7 +748,20 @@ sub exit_gracefully { undef $i3_pid; } -# Gets the socket path from the I3_SOCKET_PATH atom stored on the X11 root window +=head2 get_socket_path([ $cache ]) + +Gets the socket path from the C atom stored on the X11 root +window. After the first call, this function will return a cached version of the +socket path unless you specify a false value for C<$cache>. + + my $i3 = i3(get_socket_path()); + $i3->command('nop test example')->recv; + +To avoid caching: + + my $i3 = i3(get_socket_path(0)); + +=cut sub get_socket_path { my ($cache) = @_; $cache ||= 1; @@ -504,9 +781,26 @@ sub get_socket_path { return $socketpath; } -# -# launches a new i3 process with the given string as configuration file. -# useful for tests which test specific config file directives. +=head2 launch_with_config($config, [ $args ]) + +Launches a new i3 process with C<$config> as configuration file. Useful for +tests which test specific config file directives. + + use i3test i3_autostart => 0; + + my $config = < + +=cut package i3test::X11; use parent 'X11::XCB::Connection'; From a4a3c59cf5f5e2a9e9f013aeb6a7e9e1b3b345e0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 13:31:34 +0200 Subject: [PATCH 180/200] debian: add lib-i3test.html and lib-i3test-test.html to docs --- debian/i3-wm.docs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/i3-wm.docs b/debian/i3-wm.docs index a14b8152..6bd9c5ba 100644 --- a/debian/i3-wm.docs +++ b/debian/i3-wm.docs @@ -28,3 +28,5 @@ docs/tree-shot4.png docs/refcard.html docs/refcard_style.css docs/logo-30.png +docs/lib-i3test.html +docs/lib-i3test-test.html From d745b4726965155c6e347ffecaf3d6e0ef2b9a1c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 14:08:52 +0200 Subject: [PATCH 181/200] fix typo --- testcases/lib/i3test.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index 2d38c775..12f81ea1 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -281,7 +281,7 @@ watching the testcases. =item event_mask -An arrayref contining strings which describe the X11 event mask we use for that +An arrayref containing strings which describe the X11 event mask we use for that window. The default is C<< [ 'structure_notify' ] >>. =item name From 338199913bf251540ae4d1c300f250af1c68c7f7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 14:09:01 +0200 Subject: [PATCH 182/200] add boilerplate to all testcases with documentation references --- testcases/t/001-tile.t | 13 +++++++++++++ testcases/t/002-i3-sync.t | 13 +++++++++++++ testcases/t/003-ipc.t | 13 +++++++++++++ testcases/t/004-unmanaged.t | 13 +++++++++++++ testcases/t/005-floating.t | 13 +++++++++++++ testcases/t/100-fullscreen.t | 13 +++++++++++++ testcases/t/101-focus.t | 13 +++++++++++++ testcases/t/102-dock.t | 13 +++++++++++++ testcases/t/103-move.t | 14 ++++++++++++++ testcases/t/104-focus-stack.t | 14 ++++++++++++++ testcases/t/105-stacking.t | 14 ++++++++++++++ testcases/t/111-goto.t | 13 +++++++++++++ testcases/t/112-floating-resize.t | 13 +++++++++++++ testcases/t/113-urgent.t | 13 +++++++++++++ testcases/t/114-client-leader.t | 13 +++++++++++++ testcases/t/115-ipc-workspaces.t | 13 +++++++++++++ testcases/t/116-nestedcons.t | 13 +++++++++++++ testcases/t/117-workspace.t | 13 +++++++++++++ testcases/t/118-openkill.t | 13 +++++++++++++ testcases/t/119-match.t | 13 +++++++++++++ testcases/t/120-multiple-cmds.t | 13 +++++++++++++ testcases/t/121-next-prev.t | 13 +++++++++++++ testcases/t/122-split.t | 13 +++++++++++++ testcases/t/124-move.t | 13 +++++++++++++ testcases/t/126-regress-close.t | 13 +++++++++++++ testcases/t/127-regress-floating-parent.t | 13 +++++++++++++ testcases/t/128-open-order.t | 13 +++++++++++++ testcases/t/129-focus-after-close.t | 13 +++++++++++++ testcases/t/130-close-empty-split.t | 13 +++++++++++++ testcases/t/131-stacking-order.t | 13 +++++++++++++ testcases/t/132-move-workspace.t | 13 +++++++++++++ testcases/t/133-size-hints.t | 13 +++++++++++++ testcases/t/134-invalid-command.t | 13 +++++++++++++ testcases/t/135-floating-focus.t | 13 +++++++++++++ testcases/t/136-floating-ws-empty.t | 17 ++++++++++++++++- testcases/t/137-floating-unmap.t | 14 ++++++++++++++ testcases/t/138-floating-attach.t | 14 ++++++++++++++ testcases/t/139-ws-numbers.t | 14 ++++++++++++++ testcases/t/140-focus-lost.t | 14 ++++++++++++++ testcases/t/141-resize.t | 14 ++++++++++++++ testcases/t/142-regress-move-floating.t | 13 +++++++++++++ testcases/t/143-regress-floating-restart.t | 13 +++++++++++++ testcases/t/144-regress-floating-resize.t | 13 +++++++++++++ testcases/t/145-flattening.t | 13 +++++++++++++ testcases/t/146-floating-reinsert.t | 13 +++++++++++++ testcases/t/147-regress-floatingmove.t | 13 +++++++++++++ testcases/t/148-regress-floatingmovews.t | 13 +++++++++++++ testcases/t/150-regress-dock-restart.t | 13 +++++++++++++ testcases/t/151-regress-float-size.t | 13 +++++++++++++ testcases/t/152-regress-level-up.t | 13 +++++++++++++ testcases/t/153-floating-originalsize.t | 13 +++++++++++++ testcases/t/154-regress-multiple-dock.t | 13 +++++++++++++ testcases/t/155-floating-split-size.t | 13 +++++++++++++ testcases/t/156-fullscreen-focus.t | 13 +++++++++++++ testcases/t/158-wm_take_focus.t | 13 +++++++++++++ testcases/t/159-socketpaths.t | 13 +++++++++++++ testcases/t/161-regress-borders-restart.t | 13 +++++++++++++ testcases/t/162-regress-dock-urgent.t | 13 +++++++++++++ testcases/t/163-wm-state.t | 13 +++++++++++++ testcases/t/164-kill-win-vs-client.t | 13 +++++++++++++ testcases/t/165-for_window.t | 13 +++++++++++++ testcases/t/166-assign.t | 13 +++++++++++++ testcases/t/167-workspace_layout.t | 13 +++++++++++++ testcases/t/168-regress-fullscreen-restart.t | 13 +++++++++++++ testcases/t/169-border-toggle.t | 13 +++++++++++++ testcases/t/170-force_focus_wrapping.t | 13 +++++++++++++ testcases/t/171-config-migrate.t | 13 +++++++++++++ testcases/t/172-start-on-named-ws.t | 14 ++++++++++++++ testcases/t/173-get-marks.t | 13 +++++++++++++ testcases/t/173-regress-focus-assign.t | 13 +++++++++++++ testcases/t/174-border-config.t | 13 +++++++++++++ testcases/t/174-regress-focus-toggle.t | 13 +++++++++++++ testcases/t/175-startup-notification.t | 13 +++++++++++++ testcases/t/176-workspace-baf.t | 14 ++++++++++++++ testcases/t/177-bar-config.t | 13 +++++++++++++ testcases/t/178-regress-workspace-open.t | 13 +++++++++++++ testcases/t/179-regress-multiple-ws.t | 13 +++++++++++++ testcases/t/180-fd-leaks.t | 13 +++++++++++++ testcases/t/181-regress-float-border.t | 13 +++++++++++++ testcases/t/182-regress-focus-dock.t | 13 +++++++++++++ testcases/t/183-config-variables.t | 13 +++++++++++++ testcases/t/184-regress-float-split-resize.t | 13 +++++++++++++ testcases/t/185-scratchpad.t | 13 +++++++++++++ testcases/t/186-regress-assign-focus-parent.t | 13 +++++++++++++ testcases/t/187-commands-parser.t | 13 +++++++++++++ testcases/t/188-regress-focus-restart.t | 13 +++++++++++++ testcases/t/189-floating-constraints.t | 13 +++++++++++++ testcases/t/190-scratchpad-diff-ws.t | 13 +++++++++++++ testcases/t/191-resize-levels.t | 14 ++++++++++++++ testcases/t/192-layout.t | 14 ++++++++++++++ testcases/t/193-ipc-version.t | 14 ++++++++++++++ testcases/t/194-regress-floating-size.t | 14 ++++++++++++++ testcases/t/195-net-active-window.t | 14 ++++++++++++++ testcases/t/196-randr-output-names.t | 14 ++++++++++++++ testcases/t/197-regression-move-vanish.t | 14 ++++++++++++++ testcases/t/500-multi-monitor.t | 13 +++++++++++++ testcases/t/501-scratchpad.t | 13 +++++++++++++ testcases/t/502-focus-output.t | 13 +++++++++++++ testcases/t/503-workspace.t | 13 +++++++++++++ testcases/t/504-move-workspace-to-output.t | 13 +++++++++++++ testcases/t/505-scratchpad-resolution.t | 13 +++++++++++++ 101 files changed, 1333 insertions(+), 1 deletion(-) diff --git a/testcases/t/001-tile.t b/testcases/t/001-tile.t index c13b87c4..61685a64 100644 --- a/testcases/t/001-tile.t +++ b/testcases/t/001-tile.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/002-i3-sync.t b/testcases/t/002-i3-sync.t index 1377ee94..7d840426 100644 --- a/testcases/t/002-i3-sync.t +++ b/testcases/t/002-i3-sync.t @@ -1,6 +1,19 @@ #!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) +# # checks if i3 supports I3_SYNC # use i3test; diff --git a/testcases/t/003-ipc.t b/testcases/t/003-ipc.t index 34359f20..020e19cc 100644 --- a/testcases/t/003-ipc.t +++ b/testcases/t/003-ipc.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/004-unmanaged.t b/testcases/t/004-unmanaged.t index e998eb46..cb173ac0 100644 --- a/testcases/t/004-unmanaged.t +++ b/testcases/t/004-unmanaged.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/005-floating.t b/testcases/t/005-floating.t index db5fb6db..2a0d9102 100644 --- a/testcases/t/005-floating.t +++ b/testcases/t/005-floating.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/100-fullscreen.t b/testcases/t/100-fullscreen.t index cee77132..81a97d06 100644 --- a/testcases/t/100-fullscreen.t +++ b/testcases/t/100-fullscreen.t @@ -1,5 +1,18 @@ #!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; use List::Util qw(first); diff --git a/testcases/t/101-focus.t b/testcases/t/101-focus.t index 8a795c46..d6ce0fb8 100644 --- a/testcases/t/101-focus.t +++ b/testcases/t/101-focus.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/102-dock.t b/testcases/t/102-dock.t index 20acf49e..1bac40f0 100644 --- a/testcases/t/102-dock.t +++ b/testcases/t/102-dock.t @@ -1,5 +1,18 @@ #!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; use X11::XCB 'PROP_MODE_REPLACE'; diff --git a/testcases/t/103-move.t b/testcases/t/103-move.t index 040faf20..0e01d90b 100644 --- a/testcases/t/103-move.t +++ b/testcases/t/103-move.t @@ -1,5 +1,19 @@ #!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) +# # Beware that this test uses workspace 9 to perform some tests (it expects # the workspace to be empty). # TODO: skip it by default? diff --git a/testcases/t/104-focus-stack.t b/testcases/t/104-focus-stack.t index 3b3fe74d..38227635 100644 --- a/testcases/t/104-focus-stack.t +++ b/testcases/t/104-focus-stack.t @@ -1,5 +1,19 @@ #!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) +# # Checks if the focus is correctly restored, when creating a floating client # over an unfocused tiling client and destroying the floating one again. diff --git a/testcases/t/105-stacking.t b/testcases/t/105-stacking.t index ec7b8df8..96c64975 100644 --- a/testcases/t/105-stacking.t +++ b/testcases/t/105-stacking.t @@ -1,5 +1,19 @@ #!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) +# # Beware that this test uses workspace 9 to perform some tests (it expects # the workspace to be empty). # TODO: skip it by default? diff --git a/testcases/t/111-goto.t b/testcases/t/111-goto.t index 078ab92c..c8064863 100644 --- a/testcases/t/111-goto.t +++ b/testcases/t/111-goto.t @@ -1,5 +1,18 @@ #!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; use File::Temp; diff --git a/testcases/t/112-floating-resize.t b/testcases/t/112-floating-resize.t index 52817d70..ec690b5e 100644 --- a/testcases/t/112-floating-resize.t +++ b/testcases/t/112-floating-resize.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/113-urgent.t b/testcases/t/113-urgent.t index 04f72c3d..10368532 100644 --- a/testcases/t/113-urgent.t +++ b/testcases/t/113-urgent.t @@ -1,5 +1,18 @@ #!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; use List::Util qw(first); diff --git a/testcases/t/114-client-leader.t b/testcases/t/114-client-leader.t index 497bad9e..63e92c3c 100644 --- a/testcases/t/114-client-leader.t +++ b/testcases/t/114-client-leader.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/115-ipc-workspaces.t b/testcases/t/115-ipc-workspaces.t index 4d9a0294..ec2ec9d2 100644 --- a/testcases/t/115-ipc-workspaces.t +++ b/testcases/t/115-ipc-workspaces.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 18e21019..79447386 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -1,5 +1,18 @@ #!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; use List::Util qw(first); diff --git a/testcases/t/117-workspace.t b/testcases/t/117-workspace.t index 1d8888c7..7991abe5 100644 --- a/testcases/t/117-workspace.t +++ b/testcases/t/117-workspace.t @@ -1,6 +1,19 @@ #!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 whether we can switch to a non-existant workspace # (necessary for further tests) # diff --git a/testcases/t/118-openkill.t b/testcases/t/118-openkill.t index e2a729c5..fa4becc4 100644 --- a/testcases/t/118-openkill.t +++ b/testcases/t/118-openkill.t @@ -1,6 +1,19 @@ #!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 whether opening an empty container and killing it again works # use List::Util qw(first); diff --git a/testcases/t/119-match.t b/testcases/t/119-match.t index b02cc1e0..7ac622c7 100644 --- a/testcases/t/119-match.t +++ b/testcases/t/119-match.t @@ -1,6 +1,19 @@ #!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 all kinds of matching methods # use i3test; diff --git a/testcases/t/120-multiple-cmds.t b/testcases/t/120-multiple-cmds.t index 088caf71..2403fe22 100644 --- a/testcases/t/120-multiple-cmds.t +++ b/testcases/t/120-multiple-cmds.t @@ -1,6 +1,19 @@ #!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 multiple commands (using ';') and multiple operations (using ',') # use i3test; diff --git a/testcases/t/121-next-prev.t b/testcases/t/121-next-prev.t index 447be315..3228b259 100644 --- a/testcases/t/121-next-prev.t +++ b/testcases/t/121-next-prev.t @@ -1,6 +1,19 @@ #!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 focus switching (next/prev) # use i3test; diff --git a/testcases/t/122-split.t b/testcases/t/122-split.t index 7f8f392d..01765e1e 100644 --- a/testcases/t/122-split.t +++ b/testcases/t/122-split.t @@ -1,6 +1,19 @@ #!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 splitting # use i3test; diff --git a/testcases/t/124-move.t b/testcases/t/124-move.t index c093e47b..739dc605 100644 --- a/testcases/t/124-move.t +++ b/testcases/t/124-move.t @@ -1,6 +1,19 @@ #!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 moving. Basically, there are four different code-paths: # 1) move a container which cannot be moved (single container on a workspace) # 2) move a container before another single container diff --git a/testcases/t/126-regress-close.t b/testcases/t/126-regress-close.t index 8aec87d7..76e7f47f 100644 --- a/testcases/t/126-regress-close.t +++ b/testcases/t/126-regress-close.t @@ -1,6 +1,19 @@ #!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: closing of floating clients did crash i3 when closing the # container which contained this client. # diff --git a/testcases/t/127-regress-floating-parent.t b/testcases/t/127-regress-floating-parent.t index c83c0809..40507b51 100644 --- a/testcases/t/127-regress-floating-parent.t +++ b/testcases/t/127-regress-floating-parent.t @@ -1,6 +1,19 @@ #!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: make a container floating, kill its parent, make it tiling again # use i3test; diff --git a/testcases/t/128-open-order.t b/testcases/t/128-open-order.t index ee58968f..e6f8069d 100644 --- a/testcases/t/128-open-order.t +++ b/testcases/t/128-open-order.t @@ -1,6 +1,19 @@ #!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) +# # Check if new containers are opened after the currently focused one instead # of always at the end use List::Util qw(first); diff --git a/testcases/t/129-focus-after-close.t b/testcases/t/129-focus-after-close.t index 5fc3786e..df226e84 100644 --- a/testcases/t/129-focus-after-close.t +++ b/testcases/t/129-focus-after-close.t @@ -1,6 +1,19 @@ #!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) +# # Check if the focus is correctly restored after closing windows. # use i3test; diff --git a/testcases/t/130-close-empty-split.t b/testcases/t/130-close-empty-split.t index bf93cc69..bcc83896 100644 --- a/testcases/t/130-close-empty-split.t +++ b/testcases/t/130-close-empty-split.t @@ -1,6 +1,19 @@ #!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) +# # Check if empty split containers are automatically closed. # use i3test; diff --git a/testcases/t/131-stacking-order.t b/testcases/t/131-stacking-order.t index 9c1e74ca..c04f1b09 100644 --- a/testcases/t/131-stacking-order.t +++ b/testcases/t/131-stacking-order.t @@ -1,6 +1,19 @@ #!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) +# # Check if stacking containers can be used independantly of # the split mode (horizontal/vertical) of the underlying # container. diff --git a/testcases/t/132-move-workspace.t b/testcases/t/132-move-workspace.t index c56a17a4..ba26c85f 100644 --- a/testcases/t/132-move-workspace.t +++ b/testcases/t/132-move-workspace.t @@ -1,6 +1,19 @@ #!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) +# # Checks if the 'move [window/container] to workspace' command works correctly # use i3test; diff --git a/testcases/t/133-size-hints.t b/testcases/t/133-size-hints.t index d3736e3c..1d2cf4ce 100644 --- a/testcases/t/133-size-hints.t +++ b/testcases/t/133-size-hints.t @@ -1,6 +1,19 @@ #!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) +# # Checks if size hints are interpreted correctly. # use i3test; diff --git a/testcases/t/134-invalid-command.t b/testcases/t/134-invalid-command.t index d58985e3..494cf367 100644 --- a/testcases/t/134-invalid-command.t +++ b/testcases/t/134-invalid-command.t @@ -1,6 +1,19 @@ #!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; diff --git a/testcases/t/135-floating-focus.t b/testcases/t/135-floating-focus.t index c7218130..f38a1472 100644 --- a/testcases/t/135-floating-focus.t +++ b/testcases/t/135-floating-focus.t @@ -1,5 +1,18 @@ #!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; diff --git a/testcases/t/136-floating-ws-empty.t b/testcases/t/136-floating-ws-empty.t index fa747718..703707aa 100644 --- a/testcases/t/136-floating-ws-empty.t +++ b/testcases/t/136-floating-ws-empty.t @@ -1,6 +1,21 @@ #!perl # vim:ts=4:sw=4:expandtab -# Regression test: when only having a floating window on a workspace, it should not be deleted. +# +# 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: when only having a floating window on a workspace, it should +# not be deleted. use i3test; diff --git a/testcases/t/137-floating-unmap.t b/testcases/t/137-floating-unmap.t index e91870bc..6861b1f9 100644 --- a/testcases/t/137-floating-unmap.t +++ b/testcases/t/137-floating-unmap.t @@ -1,5 +1,19 @@ #!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: Floating windows were not correctly unmapped when switching # to a different workspace. diff --git a/testcases/t/138-floating-attach.t b/testcases/t/138-floating-attach.t index db86e1ca..79b0b271 100644 --- a/testcases/t/138-floating-attach.t +++ b/testcases/t/138-floating-attach.t @@ -1,5 +1,19 @@ #!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: New windows were attached to the container of a floating window # if only a floating window is present on the workspace. diff --git a/testcases/t/139-ws-numbers.t b/testcases/t/139-ws-numbers.t index 78b9191a..6829a147 100644 --- a/testcases/t/139-ws-numbers.t +++ b/testcases/t/139-ws-numbers.t @@ -1,5 +1,19 @@ #!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) +# # Check if numbered workspaces and named workspaces are sorted in the right way # in get_workspaces IPC output (necessary for i3bar etc.). use i3test; diff --git a/testcases/t/140-focus-lost.t b/testcases/t/140-focus-lost.t index 3d78b1bd..0609fecb 100644 --- a/testcases/t/140-focus-lost.t +++ b/testcases/t/140-focus-lost.t @@ -1,5 +1,19 @@ #!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: Check if the focus stays the same when switching the layout # bug introduced by 77d0d42ed2d7ac8cafe267c92b35a81c1b9491eb use i3test; diff --git a/testcases/t/141-resize.t b/testcases/t/141-resize.t index e769ced7..e038a87b 100644 --- a/testcases/t/141-resize.t +++ b/testcases/t/141-resize.t @@ -1,5 +1,19 @@ #!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 resizing tiling containers use i3test; diff --git a/testcases/t/142-regress-move-floating.t b/testcases/t/142-regress-move-floating.t index 6b2df806..817b6ae4 100644 --- a/testcases/t/142-regress-move-floating.t +++ b/testcases/t/142-regress-move-floating.t @@ -1,6 +1,19 @@ #!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: move a floating window to a different workspace crashes i3 # use i3test; diff --git a/testcases/t/143-regress-floating-restart.t b/testcases/t/143-regress-floating-restart.t index 03d9ec12..00f0f541 100644 --- a/testcases/t/143-regress-floating-restart.t +++ b/testcases/t/143-regress-floating-restart.t @@ -1,6 +1,19 @@ #!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: floating windows are tiling after restarting, closing them crashes i3 # use i3test; diff --git a/testcases/t/144-regress-floating-resize.t b/testcases/t/144-regress-floating-resize.t index 03318d7a..6e42c883 100644 --- a/testcases/t/144-regress-floating-resize.t +++ b/testcases/t/144-regress-floating-resize.t @@ -1,6 +1,19 @@ #!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: when resizing two containers on a workspace, opening a floating # client, then closing it again, i3 will re-distribute the space on the # workspace as if a tiling container was closed, leading to the containers diff --git a/testcases/t/145-flattening.t b/testcases/t/145-flattening.t index dbd1f246..33d9f1d1 100644 --- a/testcases/t/145-flattening.t +++ b/testcases/t/145-flattening.t @@ -1,6 +1,19 @@ #!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) +# # by moving the window in the opposite orientation that its parent has, we # force i3 to create a new split container with the appropriate orientation. # However, when doing that two times in a row, we end up with two split diff --git a/testcases/t/146-floating-reinsert.t b/testcases/t/146-floating-reinsert.t index ca209e1c..e6158f86 100644 --- a/testcases/t/146-floating-reinsert.t +++ b/testcases/t/146-floating-reinsert.t @@ -1,6 +1,19 @@ #!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; my $tmp = fresh_workspace; diff --git a/testcases/t/147-regress-floatingmove.t b/testcases/t/147-regress-floatingmove.t index ff63711c..7166aef2 100644 --- a/testcases/t/147-regress-floatingmove.t +++ b/testcases/t/147-regress-floatingmove.t @@ -1,6 +1,19 @@ #!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 for moving a con outside of a floating con when there are no # tiling cons on a workspace # diff --git a/testcases/t/148-regress-floatingmovews.t b/testcases/t/148-regress-floatingmovews.t index 3d71b500..248a8ffa 100644 --- a/testcases/t/148-regress-floatingmovews.t +++ b/testcases/t/148-regress-floatingmovews.t @@ -1,6 +1,19 @@ #!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 for correct focus behaviour when moving a floating con to # another workspace. # diff --git a/testcases/t/150-regress-dock-restart.t b/testcases/t/150-regress-dock-restart.t index 3cda6059..cafbaffb 100644 --- a/testcases/t/150-regress-dock-restart.t +++ b/testcases/t/150-regress-dock-restart.t @@ -1,6 +1,19 @@ #!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 for inplace restarting with dock clients # use i3test; diff --git a/testcases/t/151-regress-float-size.t b/testcases/t/151-regress-float-size.t index 881ef8c1..c0fb3a7e 100644 --- a/testcases/t/151-regress-float-size.t +++ b/testcases/t/151-regress-float-size.t @@ -1,6 +1,19 @@ #!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 for setting a window to floating, tiling and opening a new window # use i3test; diff --git a/testcases/t/152-regress-level-up.t b/testcases/t/152-regress-level-up.t index 01009133..771a9f07 100644 --- a/testcases/t/152-regress-level-up.t +++ b/testcases/t/152-regress-level-up.t @@ -1,6 +1,19 @@ #!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 for using level-up to get to the 'content'-container and # toggle floating # diff --git a/testcases/t/153-floating-originalsize.t b/testcases/t/153-floating-originalsize.t index 83f3e85d..d2cf206d 100644 --- a/testcases/t/153-floating-originalsize.t +++ b/testcases/t/153-floating-originalsize.t @@ -1,6 +1,19 @@ #!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) +# # Test if the requested width/height is set after making the window floating. # use i3test; diff --git a/testcases/t/154-regress-multiple-dock.t b/testcases/t/154-regress-multiple-dock.t index 76577fb3..3bb0dd94 100644 --- a/testcases/t/154-regress-multiple-dock.t +++ b/testcases/t/154-regress-multiple-dock.t @@ -1,6 +1,19 @@ #!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 for closing one of multiple dock clients # use i3test; diff --git a/testcases/t/155-floating-split-size.t b/testcases/t/155-floating-split-size.t index 76c31af6..7475f9c7 100644 --- a/testcases/t/155-floating-split-size.t +++ b/testcases/t/155-floating-split-size.t @@ -1,6 +1,19 @@ #!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) +# # Test to see if i3 combines the geometry of all children in a split container # when setting the split container to floating # diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index b779ce7d..29a410d2 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -1,6 +1,19 @@ #!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) +# # Test if new containers get focused when there is a fullscreen container at # the time of launching the new one. Also make sure that focusing containers # in other workspaces work even when there is a fullscreen container. diff --git a/testcases/t/158-wm_take_focus.t b/testcases/t/158-wm_take_focus.t index c4d30575..222c93e4 100644 --- a/testcases/t/158-wm_take_focus.t +++ b/testcases/t/158-wm_take_focus.t @@ -1,6 +1,19 @@ #!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 if the WM_TAKE_FOCUS protocol is correctly handled by i3 # use i3test; diff --git a/testcases/t/159-socketpaths.t b/testcases/t/159-socketpaths.t index 7c3946b4..d21581d1 100644 --- a/testcases/t/159-socketpaths.t +++ b/testcases/t/159-socketpaths.t @@ -1,6 +1,19 @@ #!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 if the various ipc_socket_path options are correctly handled # use i3test i3_autostart => 0; diff --git a/testcases/t/161-regress-borders-restart.t b/testcases/t/161-regress-borders-restart.t index 9ae677e7..1db64575 100644 --- a/testcases/t/161-regress-borders-restart.t +++ b/testcases/t/161-regress-borders-restart.t @@ -1,6 +1,19 @@ #!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 to check if borders are correctly restored after an inplace # restart. # found in eb8ad348b28e243cba1972e802ca8ee636472fc9 diff --git a/testcases/t/162-regress-dock-urgent.t b/testcases/t/162-regress-dock-urgent.t index 3562ba7a..6c349aad 100644 --- a/testcases/t/162-regress-dock-urgent.t +++ b/testcases/t/162-regress-dock-urgent.t @@ -1,6 +1,19 @@ #!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 for setting the urgent hint on dock clients. # found in 4be3178d4d360c2996217d811e61161c84d25898 # diff --git a/testcases/t/163-wm-state.t b/testcases/t/163-wm-state.t index 6df2bcbd..a5966030 100644 --- a/testcases/t/163-wm-state.t +++ b/testcases/t/163-wm-state.t @@ -1,6 +1,19 @@ #!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 if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when # unmapped. # diff --git a/testcases/t/164-kill-win-vs-client.t b/testcases/t/164-kill-win-vs-client.t index bce6b23b..be30ca8f 100644 --- a/testcases/t/164-kill-win-vs-client.t +++ b/testcases/t/164-kill-win-vs-client.t @@ -1,6 +1,19 @@ #!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 if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when # unmapped. # diff --git a/testcases/t/165-for_window.t b/testcases/t/165-for_window.t index eb266c2b..b01de91d 100644 --- a/testcases/t/165-for_window.t +++ b/testcases/t/165-for_window.t @@ -1,6 +1,19 @@ #!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); diff --git a/testcases/t/166-assign.t b/testcases/t/166-assign.t index 3a8a10d5..a06bb59d 100644 --- a/testcases/t/166-assign.t +++ b/testcases/t/166-assign.t @@ -1,6 +1,19 @@ #!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 if assignments work # use i3test i3_autostart => 0; diff --git a/testcases/t/167-workspace_layout.t b/testcases/t/167-workspace_layout.t index ee6c9706..033a31f2 100644 --- a/testcases/t/167-workspace_layout.t +++ b/testcases/t/167-workspace_layout.t @@ -1,6 +1,19 @@ #!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 the workspace_layout config option. # diff --git a/testcases/t/168-regress-fullscreen-restart.t b/testcases/t/168-regress-fullscreen-restart.t index ec6d4821..ec8c41c8 100644 --- a/testcases/t/168-regress-fullscreen-restart.t +++ b/testcases/t/168-regress-fullscreen-restart.t @@ -1,6 +1,19 @@ #!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) +# # Verifies that i3 survives inplace restarts with fullscreen containers # use i3test; diff --git a/testcases/t/169-border-toggle.t b/testcases/t/169-border-toggle.t index aec8df6c..7377194d 100644 --- a/testcases/t/169-border-toggle.t +++ b/testcases/t/169-border-toggle.t @@ -1,6 +1,19 @@ #!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 if the 'border toggle' command works correctly # use i3test; diff --git a/testcases/t/170-force_focus_wrapping.t b/testcases/t/170-force_focus_wrapping.t index 7949ce66..fd086505 100644 --- a/testcases/t/170-force_focus_wrapping.t +++ b/testcases/t/170-force_focus_wrapping.t @@ -1,6 +1,19 @@ #!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 if the 'force_focus_wrapping' config directive works correctly. # use i3test i3_autostart => 0; diff --git a/testcases/t/171-config-migrate.t b/testcases/t/171-config-migrate.t index dd0cbc60..a0363ef3 100644 --- a/testcases/t/171-config-migrate.t +++ b/testcases/t/171-config-migrate.t @@ -1,6 +1,19 @@ #!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 if i3-migrate-config-to-v4 correctly migrates all config file # directives and commands # diff --git a/testcases/t/172-start-on-named-ws.t b/testcases/t/172-start-on-named-ws.t index 42a44459..9e6806a4 100644 --- a/testcases/t/172-start-on-named-ws.t +++ b/testcases/t/172-start-on-named-ws.t @@ -1,5 +1,19 @@ #!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) +# # checks if i3 starts up on workspace '1' or the first configured named workspace # use i3test i3_autostart => 0; diff --git a/testcases/t/173-get-marks.t b/testcases/t/173-get-marks.t index e8964d30..3b97feb1 100644 --- a/testcases/t/173-get-marks.t +++ b/testcases/t/173-get-marks.t @@ -1,6 +1,19 @@ #!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) +# # checks if the IPC message type get_marks works correctly # use i3test; diff --git a/testcases/t/173-regress-focus-assign.t b/testcases/t/173-regress-focus-assign.t index 22306db6..91d367d1 100644 --- a/testcases/t/173-regress-focus-assign.t +++ b/testcases/t/173-regress-focus-assign.t @@ -1,6 +1,19 @@ #!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: Checks if focus is stolen when a window is managed which is # assigned to an invisible workspace # diff --git a/testcases/t/174-border-config.t b/testcases/t/174-border-config.t index 2586657b..6e837cf0 100644 --- a/testcases/t/174-border-config.t +++ b/testcases/t/174-border-config.t @@ -1,6 +1,19 @@ #!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 the new_window and new_float config option. # diff --git a/testcases/t/174-regress-focus-toggle.t b/testcases/t/174-regress-focus-toggle.t index 469d1be8..192e9753 100644 --- a/testcases/t/174-regress-focus-toggle.t +++ b/testcases/t/174-regress-focus-toggle.t @@ -1,6 +1,19 @@ #!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: Checks if i3 still lives after using 'focus mode_toggle' on an # empty workspace. This regression was fixed in # 0848844f2d41055f6ffc69af1149d7a873460976. diff --git a/testcases/t/175-startup-notification.t b/testcases/t/175-startup-notification.t index 2c6ce353..b27a9a70 100644 --- a/testcases/t/175-startup-notification.t +++ b/testcases/t/175-startup-notification.t @@ -1,6 +1,19 @@ #!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) +# # Test for the startup notification protocol. # diff --git a/testcases/t/176-workspace-baf.t b/testcases/t/176-workspace-baf.t index 80644563..07c3c84a 100644 --- a/testcases/t/176-workspace-baf.t +++ b/testcases/t/176-workspace-baf.t @@ -1,5 +1,19 @@ #!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) +# # Checks if the 'workspace back_and_forth' command and the # 'workspace_auto_back_and_forth' config directive work correctly. # diff --git a/testcases/t/177-bar-config.t b/testcases/t/177-bar-config.t index 3caa6696..762e52b8 100644 --- a/testcases/t/177-bar-config.t +++ b/testcases/t/177-bar-config.t @@ -1,6 +1,19 @@ #!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) +# # Checks that the bar config is parsed correctly. # diff --git a/testcases/t/178-regress-workspace-open.t b/testcases/t/178-regress-workspace-open.t index 25fe7d9a..53e67bdc 100644 --- a/testcases/t/178-regress-workspace-open.t +++ b/testcases/t/178-regress-workspace-open.t @@ -1,6 +1,19 @@ #!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 if empty workspaces are closed when the last child # exits, as long as they're not empty. # diff --git a/testcases/t/179-regress-multiple-ws.t b/testcases/t/179-regress-multiple-ws.t index be3cc687..ae442023 100644 --- a/testcases/t/179-regress-multiple-ws.t +++ b/testcases/t/179-regress-multiple-ws.t @@ -1,6 +1,19 @@ #!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) +# # The command "move workspace prev; workspace prev" will lead to an error. # This regression is present in 7f9b65f6a752e454c492447be4e21e2ee8faf8fd use i3test; diff --git a/testcases/t/180-fd-leaks.t b/testcases/t/180-fd-leaks.t index 487803c4..454bfe7d 100644 --- a/testcases/t/180-fd-leaks.t +++ b/testcases/t/180-fd-leaks.t @@ -1,6 +1,19 @@ #!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) +# # Verifies that i3 does not leak any file descriptors in 'exec'. # use i3test; diff --git a/testcases/t/181-regress-float-border.t b/testcases/t/181-regress-float-border.t index f77f780a..c6a05424 100644 --- a/testcases/t/181-regress-float-border.t +++ b/testcases/t/181-regress-float-border.t @@ -1,6 +1,19 @@ #!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: Changing border style should not have an impact on the size # (geometry) of the child window. See ticket http://bugs.i3wm.org/561 # Wrong behaviour manifested itself up to (including) commit diff --git a/testcases/t/182-regress-focus-dock.t b/testcases/t/182-regress-focus-dock.t index 6212a9ea..4aaabb83 100644 --- a/testcases/t/182-regress-focus-dock.t +++ b/testcases/t/182-regress-focus-dock.t @@ -1,6 +1,19 @@ #!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: Focusing a dock window should just do nothing, not crash i3. # See ticket http://bugs.i3wm.org/575 # Wrong behaviour manifested itself up to (including) commit diff --git a/testcases/t/183-config-variables.t b/testcases/t/183-config-variables.t index 1da25a65..8fbbff70 100644 --- a/testcases/t/183-config-variables.t +++ b/testcases/t/183-config-variables.t @@ -1,6 +1,19 @@ #!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) +# # Checks that variables are parsed correctly by using for_window rules with # variables in it. # diff --git a/testcases/t/184-regress-float-split-resize.t b/testcases/t/184-regress-float-split-resize.t index 1a21f2b2..d637baf3 100644 --- a/testcases/t/184-regress-float-split-resize.t +++ b/testcases/t/184-regress-float-split-resize.t @@ -1,6 +1,19 @@ #!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: resizing a floating split container leads to a crash. # (Ticket #588, present until 4412ccbe5a4fad8a4cd594e6f10f937515a4d37c) # diff --git a/testcases/t/185-scratchpad.t b/testcases/t/185-scratchpad.t index 06debab3..87bda529 100644 --- a/testcases/t/185-scratchpad.t +++ b/testcases/t/185-scratchpad.t @@ -1,6 +1,19 @@ #!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 the scratchpad functionality. # use i3test; diff --git a/testcases/t/186-regress-assign-focus-parent.t b/testcases/t/186-regress-assign-focus-parent.t index 4f4db430..7562ad90 100644 --- a/testcases/t/186-regress-assign-focus-parent.t +++ b/testcases/t/186-regress-assign-focus-parent.t @@ -1,6 +1,19 @@ #!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: New windows were not opened in the correct place if they # matched an assignment. # Wrong behaviour manifested itself up to (including) commit diff --git a/testcases/t/187-commands-parser.t b/testcases/t/187-commands-parser.t index 335c775d..37deb942 100644 --- a/testcases/t/187-commands-parser.t +++ b/testcases/t/187-commands-parser.t @@ -1,6 +1,19 @@ #!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 the standalone parser binary to see if it calls the right code when # confronted with various commands, if it prints proper error messages for # wrong commands and if it terminates in every case. diff --git a/testcases/t/188-regress-focus-restart.t b/testcases/t/188-regress-focus-restart.t index 1de9f366..3d602c11 100644 --- a/testcases/t/188-regress-focus-restart.t +++ b/testcases/t/188-regress-focus-restart.t @@ -1,6 +1,19 @@ #!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) +# # Verifies that i3 survives inplace restarts with fullscreen containers # use i3test; diff --git a/testcases/t/189-floating-constraints.t b/testcases/t/189-floating-constraints.t index 9b6fb150..a3ce8476 100644 --- a/testcases/t/189-floating-constraints.t +++ b/testcases/t/189-floating-constraints.t @@ -1,6 +1,19 @@ #!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 the floating_{minimum,maximum}_size config options. # # Note that the minimum floating window size is already verified in diff --git a/testcases/t/190-scratchpad-diff-ws.t b/testcases/t/190-scratchpad-diff-ws.t index ed03f210..5451f482 100644 --- a/testcases/t/190-scratchpad-diff-ws.t +++ b/testcases/t/190-scratchpad-diff-ws.t @@ -1,6 +1,19 @@ #!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) +# # Test for ticket #676: 'scratchpad show' causes a segfault if the scratchpad # window is shown on another workspace. # diff --git a/testcases/t/191-resize-levels.t b/testcases/t/191-resize-levels.t index 9eef6c0c..559a93e9 100644 --- a/testcases/t/191-resize-levels.t +++ b/testcases/t/191-resize-levels.t @@ -1,5 +1,19 @@ #!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) +# # Verifies that you can resize across different levels of containers even when # they are all of the same orientation. # (Ticket #754) diff --git a/testcases/t/192-layout.t b/testcases/t/192-layout.t index e410d513..6fd6eae8 100644 --- a/testcases/t/192-layout.t +++ b/testcases/t/192-layout.t @@ -1,5 +1,19 @@ #!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) +# # Verifies that switching between the different layouts works as expected. use i3test; diff --git a/testcases/t/193-ipc-version.t b/testcases/t/193-ipc-version.t index 172dd8f0..d2e082ec 100644 --- a/testcases/t/193-ipc-version.t +++ b/testcases/t/193-ipc-version.t @@ -1,5 +1,19 @@ #!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) +# # Verifies that we can get the version number of i3 via IPC. use i3test; diff --git a/testcases/t/194-regress-floating-size.t b/testcases/t/194-regress-floating-size.t index ccbfaae5..dc6739e5 100644 --- a/testcases/t/194-regress-floating-size.t +++ b/testcases/t/194-regress-floating-size.t @@ -1,5 +1,19 @@ #!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) +# # Verifies that the size requested by floating windows is set by i3, no matter # to which value the new_window option is set. # ticket #770, bug still present in commit ae88accf6fe3817ff42d0d51be1965071194766e diff --git a/testcases/t/195-net-active-window.t b/testcases/t/195-net-active-window.t index f392378a..c62d4fda 100644 --- a/testcases/t/195-net-active-window.t +++ b/testcases/t/195-net-active-window.t @@ -1,5 +1,19 @@ #!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) +# # Verifies that the _NET_ACTIVE_WINDOW message only changes focus when the # window is on a visible workspace. # ticket #774, bug still present in commit 1e49f1b08a3035c1f238fcd6615e332216ab582e diff --git a/testcases/t/196-randr-output-names.t b/testcases/t/196-randr-output-names.t index dfc288d9..e5049eb8 100644 --- a/testcases/t/196-randr-output-names.t +++ b/testcases/t/196-randr-output-names.t @@ -1,5 +1,19 @@ #!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) +# # Verify that i3 allows strange RandR output names such as DVI-I_1/digital. # Ticket: #785 # Bug still in: 4.2-256-ga007283 diff --git a/testcases/t/197-regression-move-vanish.t b/testcases/t/197-regression-move-vanish.t index 41c7b87e..0307769b 100644 --- a/testcases/t/197-regression-move-vanish.t +++ b/testcases/t/197-regression-move-vanish.t @@ -1,5 +1,19 @@ #!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: moving a window to the right out of a splitv container would # make it vanish. # Ticket: #790 diff --git a/testcases/t/500-multi-monitor.t b/testcases/t/500-multi-monitor.t index 5c341d63..3df2ba3f 100644 --- a/testcases/t/500-multi-monitor.t +++ b/testcases/t/500-multi-monitor.t @@ -1,6 +1,19 @@ #!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 the provided X-Server to the t/5??-*.t tests is actually providing # multiple monitors. # diff --git a/testcases/t/501-scratchpad.t b/testcases/t/501-scratchpad.t index 9305239a..0f9b0df0 100644 --- a/testcases/t/501-scratchpad.t +++ b/testcases/t/501-scratchpad.t @@ -1,6 +1,19 @@ #!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) +# # Verifies that scratchpad windows show up on the proper output. # ticket #596, bug present until up to commit # 89dded044b4fffe78f9d70778748fabb7ac533e9. diff --git a/testcases/t/502-focus-output.t b/testcases/t/502-focus-output.t index 4e0fedbb..a6c5583f 100644 --- a/testcases/t/502-focus-output.t +++ b/testcases/t/502-focus-output.t @@ -1,6 +1,19 @@ #!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) +# # Verifies the 'focus output' command works properly. use i3test i3_autostart => 0; diff --git a/testcases/t/503-workspace.t b/testcases/t/503-workspace.t index 94ba3434..20d4fd2b 100644 --- a/testcases/t/503-workspace.t +++ b/testcases/t/503-workspace.t @@ -1,6 +1,19 @@ #!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 whether 'workspace next_on_output' and the like work correctly. # use List::Util qw(first); diff --git a/testcases/t/504-move-workspace-to-output.t b/testcases/t/504-move-workspace-to-output.t index 8357e0da..7a976271 100644 --- a/testcases/t/504-move-workspace-to-output.t +++ b/testcases/t/504-move-workspace-to-output.t @@ -1,6 +1,19 @@ #!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 whether the 'move workspace to [output] ' command works # use List::Util qw(first); diff --git a/testcases/t/505-scratchpad-resolution.t b/testcases/t/505-scratchpad-resolution.t index ce85cfc0..de13a544 100644 --- a/testcases/t/505-scratchpad-resolution.t +++ b/testcases/t/505-scratchpad-resolution.t @@ -1,6 +1,19 @@ #!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) +# # Verifies that scratchpad windows don’t move due to floating point caulcation # errors when repeatedly hiding/showing, no matter what display resolution. # From daf2b57222a1a107825cff2699f4e7b673ddb1ed Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 14:13:43 +0200 Subject: [PATCH 183/200] docs/testsuite: add pointers to additional docs --- docs/testsuite | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/testsuite b/docs/testsuite index fcc9393a..4dcf1670 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -1,7 +1,7 @@ i3 testsuite ============ -Michael Stapelberg -September 2011 +Michael Stapelberg +September 2012 This document explains how the i3 testsuite works, how to use it and extend it. It is targeted at developers who not necessarily have been doing testing before @@ -33,6 +33,19 @@ able to easily test if the feature is working correctly. Many developers will test manually if everything works. Having a testcase not only helps you with that, but it will also be useful for every future change. +== Relevant documentation + +Apart from this document, you should also have a look at: + +1. The "Modern Perl" book, which can be found at + http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +2. The latest Perl documentation of the "i3test" (general testcase setup) and + "i3test::Test" (additional test instructions) modules: + http://build.i3wm.org/docs/lib-i3test.html respectively + http://build.i3wm.org/docs/lib-i3test-test.html +3. The latest documentation on i3’s IPC interface: + http://build.i3wm.org/docs/ipc.html + == Implementation For several reasons, the i3 testsuite has been implemented in Perl: From cfa74c5468fc93dafe4d050135339d0e5930bdd0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Sep 2012 16:49:43 +0200 Subject: [PATCH 184/200] include the testcases in the dist tarball (required for building docs) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 16b444c4..3b675034 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ dist: distclean [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2 mkdir i3-${VERSION} cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION} - cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs i3-${VERSION} + cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs testcases i3-${VERSION} # Only copy toplevel documentation (important stuff) mkdir i3-${VERSION}/docs # Pre-generate documentation From 38869004fe025454010c2da08287e2256b103d02 Mon Sep 17 00:00:00 2001 From: Bas Pape Date: Mon, 10 Sep 2012 16:47:05 +0200 Subject: [PATCH 185/200] Replace exit with backtrace in crash dialog When the user hits 'b', attach gdb and dump a backtrace to the tmpdir --- src/sighandler.c | 139 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 20 deletions(-) diff --git a/src/sighandler.c b/src/sighandler.c index 3a9307e1..fe8c9f64 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -16,11 +16,14 @@ #include #include #include +#include #include #include +static void open_popups(void); + static xcb_gcontext_t pixmap_gc; static xcb_pixmap_t pixmap; static int raised_signal; @@ -29,11 +32,84 @@ static char *crash_text[] = { "i3 just crashed.", "To debug this problem, either attach gdb now", "or press", - "- 'e' to exit and get a core-dump,", + "- 'b' to save a backtrace (needs GDB),", "- 'r' to restart i3 in-place or", "- 'f' to forget the current layout and restart" }; static int crash_text_longest = 5; +static int backtrace_string_index = 3; +static int backtrace_done = 0; + +/* + * Attach gdb to pid_parent and dump a backtrace to i3-backtrace.$pid in the + * tmpdir + */ +static int backtrace(void) { + char *tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = "/tmp"; + + pid_t pid_parent = getpid(); + + char *filename = NULL; + if (sasprintf(&filename, "%s/i3-backtrace.%d.txt", tmpdir, pid_parent) == -1 + || filename == NULL) + filename = "i3-backtrace.txt"; + + pid_t pid_gdb = fork(); + if (pid_gdb < 0) { + DLOG("Failed to fork for GDB\n"); + return -1; + } else if (pid_gdb == 0) { + /* child */ + + /* close standard streams in case i3 is started from a terminal; gdb + * needs to run without controlling terminal for it to work properly in + * this situation */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + char *pid_s = NULL; + sasprintf(&pid_s, "%d", pid_parent); + + char *gdb_log_cmd = NULL; + if (sasprintf(&gdb_log_cmd, "set logging file %s", filename) == -1 + || gdb_log_cmd == NULL) + gdb_log_cmd = "set logging file i3-backtrace.txt"; + + char *args[] = { + "gdb", + start_argv[0], + "-p", + pid_s, + "-batch", + "-nx", + "-ex", gdb_log_cmd, + "-ex", "set logging on", + "-ex", "bt full", + "-ex", "quit", + NULL + }; + execvp(args[0], args); + DLOG("Failed to exec GDB\n"); + exit(1); + } + int status = 0; + struct stat bt; + + waitpid(pid_gdb, &status, 0); + + /* see if the backtrace was succesful or not */ + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + DLOG("GDB did not run properly\n"); + return -1; + } else if (stat(filename, &bt) == -1) { + DLOG("GDB executed succesfully, but no backtrace was generated\n"); + return -1; + } + return 1; +} /* * Draw the window containing the info text @@ -51,9 +127,23 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei /* restore font color */ set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); + char *bt_colour = "#FFFFFF"; + if (backtrace_done < 0) + bt_colour = "#AA0000"; + else if (backtrace_done > 0) + bt_colour = "#00AA00"; + for (int i = 0; crash_text_i3strings[i] != NULL; ++i) { + /* fix the colour for the backtrace line when it finished */ + if (i == backtrace_string_index) + set_font_colors(pixmap_gc, get_colorpixel(bt_colour), get_colorpixel("#000000")); + draw_text(crash_text_i3strings[i], pixmap, pixmap_gc, 8, 5 + i * font_height, width - 16); + + /* and reset the colour again for other lines */ + if (i == backtrace_string_index) + set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); } /* Copy the contents of the pixmap to the real window */ @@ -64,7 +154,7 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei } /* - * Handles keypresses of 'e' or 'r' to exit or restart i3 + * Handles keypresses of 'b', 'r' and 'f' to get a backtrace or restart i3 * */ static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { @@ -77,10 +167,15 @@ static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_p xcb_keysym_t sym = xcb_key_press_lookup_keysym(keysyms, event, state); - if (sym == 'e') { - DLOG("User issued exit-command, raising error again.\n"); - raise(raised_signal); - exit(1); + if (sym == 'b') { + DLOG("User issued core-dump command.\n"); + + /* fork and exec/attach GDB to the parent to get a backtrace in the + * tmpdir */ + backtrace_done = backtrace(); + + /* re-open the windows to indicate that it's finished */ + open_popups(); } if (sym == 'r') @@ -129,20 +224,7 @@ static xcb_window_t open_input_window(xcb_connection_t *conn, Rect screen_rect, return win; } -/* - * Handle signals - * It creates a window asking the user to restart in-place - * or exit to generate a core dump - * - */ -void handle_signal(int sig, siginfo_t *info, void *data) { - DLOG("i3 crashed. SIG: %d\n", sig); - - struct sigaction action; - action.sa_handler = SIG_DFL; - sigaction(sig, &action, NULL); - raised_signal = sig; - +static void open_popups() { /* width and height of the popup window, so that the text fits in */ int crash_text_num = sizeof(crash_text) / sizeof(char*); int height = 13 + (crash_text_num * config.font.height); @@ -182,6 +264,23 @@ void handle_signal(int sig, siginfo_t *info, void *data) { sig_draw_window(win, width, height, config.font.height, crash_text_i3strings); xcb_flush(conn); } +} + +/* + * Handle signals + * It creates a window asking the user to restart in-place + * or exit to generate a core dump + * + */ +void handle_signal(int sig, siginfo_t *info, void *data) { + DLOG("i3 crashed. SIG: %d\n", sig); + + struct sigaction action; + action.sa_handler = SIG_DFL; + sigaction(sig, &action, NULL); + raised_signal = sig; + + open_popups(); xcb_generic_event_t *event; /* Yay, more own eventhandlers… */ From 9d8a4ebc6182ae5ef6e873906e614a50d6cf9c84 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 12:57:53 +0200 Subject: [PATCH 186/200] sighandler: provide gdb with pipe stdin/stdout fds (necessary for gdb < 7.5) --- src/sighandler.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sighandler.c b/src/sighandler.c index fe8c9f64..5d987865 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -62,6 +62,11 @@ static int backtrace(void) { return -1; } else if (pid_gdb == 0) { /* child */ + int stdin_pipe[2], + stdout_pipe[2]; + + pipe(stdin_pipe); + pipe(stdout_pipe); /* close standard streams in case i3 is started from a terminal; gdb * needs to run without controlling terminal for it to work properly in @@ -70,6 +75,12 @@ static int backtrace(void) { close(STDOUT_FILENO); close(STDERR_FILENO); + /* We provide pipe file descriptors for stdin/stdout because gdb < 7.5 + * crashes otherwise, see + * http://sourceware.org/bugzilla/show_bug.cgi?id=14114 */ + dup2(stdin_pipe[0], STDIN_FILENO); + dup2(stdout_pipe[1], STDOUT_FILENO); + char *pid_s = NULL; sasprintf(&pid_s, "%d", pid_parent); From e713283605a8636541cc91987436abce8902bcf8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 13:02:59 +0200 Subject: [PATCH 187/200] =?UTF-8?q?sasprintf()=20already=20handles=20error?= =?UTF-8?q?s,=20we=20don=E2=80=99t=20need=20to=20do=20that=20twice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sighandler.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/sighandler.c b/src/sighandler.c index 5d987865..c951ea9d 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -52,9 +52,7 @@ static int backtrace(void) { pid_t pid_parent = getpid(); char *filename = NULL; - if (sasprintf(&filename, "%s/i3-backtrace.%d.txt", tmpdir, pid_parent) == -1 - || filename == NULL) - filename = "i3-backtrace.txt"; + sasprintf(&filename, "%s/i3-backtrace.%d.txt", tmpdir, pid_parent); pid_t pid_gdb = fork(); if (pid_gdb < 0) { @@ -81,13 +79,9 @@ static int backtrace(void) { dup2(stdin_pipe[0], STDIN_FILENO); dup2(stdout_pipe[1], STDOUT_FILENO); - char *pid_s = NULL; + char *pid_s, *gdb_log_cmd; sasprintf(&pid_s, "%d", pid_parent); - - char *gdb_log_cmd = NULL; - if (sasprintf(&gdb_log_cmd, "set logging file %s", filename) == -1 - || gdb_log_cmd == NULL) - gdb_log_cmd = "set logging file i3-backtrace.txt"; + sasprintf(&gdb_log_cmd, "set logging file %s", filename); char *args[] = { "gdb", From 0db93d9072de081c28d08dbbd9075e9fa2264c63 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 13:07:20 +0200 Subject: [PATCH 188/200] sighandler: use non-existing backtrace filenames ($TMPDIR/i3-backtrace.%pid.%idx.txt) --- src/sighandler.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sighandler.c b/src/sighandler.c index c951ea9d..988927f0 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -52,7 +52,15 @@ static int backtrace(void) { pid_t pid_parent = getpid(); char *filename = NULL; - sasprintf(&filename, "%s/i3-backtrace.%d.txt", tmpdir, pid_parent); + int suffix = 0; + struct stat bt; + /* Find a unique filename for the backtrace (since the PID of i3 stays the + * same), so that we don’t overwrite earlier backtraces. */ + do { + FREE(filename); + sasprintf(&filename, "%s/i3-backtrace.%d.%d.txt", tmpdir, pid_parent, suffix); + suffix++; + } while (stat(filename, &bt) == 0); pid_t pid_gdb = fork(); if (pid_gdb < 0) { @@ -101,7 +109,6 @@ static int backtrace(void) { exit(1); } int status = 0; - struct stat bt; waitpid(pid_gdb, &status, 0); From 40b12c0a4d15ca44bdcf23b4c0e297f67ddf4b1b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 13:11:47 +0200 Subject: [PATCH 189/200] Remove support for resize increment size hints for tiling windows As discussed on the mailing list and the bugtracker. fixes #540 --- src/render.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/render.c b/src/render.c index 01628c35..da993a57 100644 --- a/src/render.c +++ b/src/render.c @@ -174,19 +174,11 @@ void render_con(Con *con, bool render_fullscreen) { inset->width = new_width; } - if (con->height_increment > 1) { - int old_height = inset->height; - inset->height -= (inset->height - con->base_height) % con->height_increment; - DLOG("Lost %d pixel due to client's height_increment (%d px, base_height = %d)\n", - old_height - inset->height, con->height_increment, con->base_height); - } - - if (con->width_increment > 1) { - int old_width = inset->width; - inset->width -= (inset->width - con->base_width) % con->width_increment; - DLOG("Lost %d pixel due to client's width_increment (%d px, base_width = %d)\n", - old_width - inset->width, con->width_increment, con->base_width); - } + /* NB: We used to respect resize increment size hints for tiling + * windows up until commit 0db93d9 here. However, since all terminal + * emulators cope with ignoring the size hints in a better way than we + * can (by providing their fake-transparency or background color), this + * code was removed. See also http://bugs.i3wm.org/540 */ DLOG("child will be at %dx%d with size %dx%d\n", inset->x, inset->y, inset->width, inset->height); } From 8abd1c48f73add24b8c935e5ea5fdaef778b8bee Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 13:17:36 +0200 Subject: [PATCH 190/200] Only launch i3-nagbar for parse errors --- src/commands_parser.c | 6 +++++- src/key_press.c | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/commands_parser.c b/src/commands_parser.c index 20a7d67a..d739f4e1 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -204,7 +204,6 @@ static void next_state(const cmdp_token *token) { } } -/* TODO: Return parsing errors via JSON. */ struct CommandResult *parse_command(const char *input) { DLOG("COMMAND: *%s*\n", input); state = INITIAL; @@ -386,6 +385,11 @@ struct CommandResult *parse_command(const char *input) { y(map_open); ystr("success"); y(bool, false); + /* We set parse_error to true to distinguish this from other + * errors. i3-nagbar is spawned upon keypresses only for parser + * errors. */ + ystr("parse_error"); + y(bool, true); ystr("error"); ystr(errormessage); ystr("input"); diff --git a/src/key_press.c b/src/key_press.c index 3f466a2e..5919e64c 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -16,7 +16,7 @@ #include "all.h" static int current_nesting_level; -static bool success_key; +static bool parse_error_key; static bool command_failed; /* XXX: I don’t want to touch too much of the nagbar code at once, but we @@ -184,9 +184,9 @@ void kill_commanderror_nagbar(bool wait_for_it) { } static int json_boolean(void *ctx, int boolval) { - DLOG("Got bool: %d, success_key %d, nesting_level %d\n", boolval, success_key, current_nesting_level); + DLOG("Got bool: %d, parse_error_key %d, nesting_level %d\n", boolval, parse_error_key, current_nesting_level); - if (success_key && current_nesting_level == 1 && !boolval) + if (parse_error_key && current_nesting_level == 1 && boolval) command_failed = true; return 1; @@ -197,8 +197,8 @@ static int json_map_key(void *ctx, const unsigned char *stringval, size_t string #else static int json_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) { #endif - success_key = (stringlen >= strlen("success") && - strncmp((const char*)stringval, "success", strlen("success")) == 0); + parse_error_key = (stringlen >= strlen("parse_error") && + strncmp((const char*)stringval, "parse_error", strlen("parse_error")) == 0); return 1; } @@ -296,7 +296,7 @@ void handle_key_press(xcb_key_press_event_t *event) { yajl_gen_get_buf(command_output->json_gen, &reply, &length); current_nesting_level = 0; - success_key = false; + parse_error_key = false; command_failed = false; yajl_status state = yajl_parse(handle, reply, length); if (state != yajl_status_ok) { From 22922a9e7094fd029a5d356bd48237dae3b5a435 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Sep 2012 13:19:05 +0200 Subject: [PATCH 191/200] update release notes to reflect the current i3-nagbar situation --- RELEASE-NOTES-4.3 | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/RELEASE-NOTES-4.3 b/RELEASE-NOTES-4.3 index 2b62042e..d828a0dc 100644 --- a/RELEASE-NOTES-4.3 +++ b/RELEASE-NOTES-4.3 @@ -6,19 +6,10 @@ This is the third release of the new major version of i3, v4.3. It is considered stable. All users of i3 are strongly encouraged to upgrade. -One of the most visible changes is probably that commands which lead to an - error will now spawn i3-nagbar. This will make you immediately aware of - problems such as typos in your configuration file (such as "bindsym $mod+x exc - firefox" instead of "exec"). This is not restricted to parser errors, but all - errors (such as when trying to move a window to another workspace without - actually having a window focused). If this is annoying to you for some specific - key configuration, you can turn it off by replacing a binding like: - bindsym $mod+x move absolute position center - with something like this: - bindsym $mod+x exec --no-startup-id i3-msg move absolute position center >/dev/null 2>&1 - (Yes, this is somewhat painful, but intended. You should not suppress errors in - general, so we don’t want to make it too easy.) - +One rather visible change is that commands which could not be parsed properly + will now spawn i3-nagbar. In case you used "bindsym $mod+x firefox" (and + forgot the "exec" keyword) or you made a typo in your config, you will now + notice that :). We also made the orientation (horizontal/vertical) part of the layout mechanism: Before, we got the default layout and you could change From cd4dd365e87ea3ea09cde9cd8a00209284dc282d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 16 Sep 2012 22:53:41 +0200 Subject: [PATCH 192/200] Allow changing the layout of workspaces by storing it (Thanks mhcerri) Before commit 4976fa33504907510f1ee9e44884c116af0d801b, setting the layout of workspaces to something else than the default would just mess up the parent container of the workspace (the content container). After that commit, it would create an unnecessary split container when you change the layout _before_ opening any containers. To avoid this, we now store the layout (similar to how the 'workspace_layout' configuration directive works) and apply it when the first container is attached to the workspace. Fixes #796 --- include/data.h | 16 ++++++++- src/con.c | 64 +++++++++++++++++++----------------- src/ipc.c | 17 ++++++++++ src/load_layout.c | 11 +++++++ src/workspace.c | 5 +-- testcases/t/116-nestedcons.t | 1 + 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/include/data.h b/include/data.h index e8df78c7..02f781c9 100644 --- a/include/data.h +++ b/include/data.h @@ -514,6 +514,20 @@ struct Con { TAILQ_HEAD(swallow_head, Match) swallow_head; enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode; + /* layout is the layout of this container: one of split[v|h], stacked or + * tabbed. Special containers in the tree (above workspaces) have special + * layouts like dockarea or output. + * + * last_split_layout is one of splitv or splith to support the old "layout + * default" command which by now should be "layout splitv" or "layout + * splith" explicitly. + * + * workspace_layout is only for type == CT_WORKSPACE cons. When you change + * the layout of a workspace without any children, i3 cannot just set the + * layout (because workspaces need to be splitv/splith to allow focus + * parent and opening new containers). Instead, it stores the requested + * layout in workspace_layout and creates a new split container with that + * layout whenever a new container is attached to the workspace. */ enum { L_DEFAULT = 0, L_STACKED = 1, @@ -522,7 +536,7 @@ struct Con { L_OUTPUT = 4, L_SPLITV = 5, L_SPLITH = 6 - } layout, last_split_layout; + } layout, last_split_layout, workspace_layout; border_style_t border_style; /** floating? (= not in tiling layout) This cannot be simply a bool * because we want to keep track of whether the status was set by the diff --git a/src/con.c b/src/con.c index 1d86d73a..f5ccfcdd 100644 --- a/src/con.c +++ b/src/con.c @@ -135,7 +135,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) { */ if (con->window != NULL && parent->type == CT_WORKSPACE && - config.default_layout != L_DEFAULT) { + parent->workspace_layout != L_DEFAULT) { DLOG("Parent is a workspace. Applying default layout...\n"); Con *target = workspace_attach_to(parent); @@ -1105,39 +1105,43 @@ void con_set_layout(Con *con, int layout) { * need to create a new split container. */ if (con->type == CT_WORKSPACE && (layout == L_STACKED || layout == L_TABBED)) { - DLOG("Creating new split container\n"); - /* 1: create a new split container */ - Con *new = con_new(NULL, NULL); - new->parent = con; + if (con_num_children(con) == 0) { + DLOG("Setting workspace_layout to %d\n", layout); + con->workspace_layout = layout; + } else { + DLOG("Creating new split container\n"); + /* 1: create a new split container */ + Con *new = con_new(NULL, NULL); + new->parent = con; - /* 2: Set the requested layout on the split container and mark it as - * split. */ - new->layout = layout; - new->last_split_layout = con->last_split_layout; - new->split = true; + /* 2: Set the requested layout on the split container and mark it as + * split. */ + new->layout = layout; + new->last_split_layout = con->last_split_layout; + new->split = true; - Con *old_focused = TAILQ_FIRST(&(con->focus_head)); - if (old_focused == TAILQ_END(&(con->focus_head))) - old_focused = NULL; + Con *old_focused = TAILQ_FIRST(&(con->focus_head)); + if (old_focused == TAILQ_END(&(con->focus_head))) + old_focused = NULL; - /* 3: move the existing cons of this workspace below the new con */ - DLOG("Moving cons\n"); - Con *child; - while (!TAILQ_EMPTY(&(con->nodes_head))) { - child = TAILQ_FIRST(&(con->nodes_head)); - con_detach(child); - con_attach(child, new, true); + /* 3: move the existing cons of this workspace below the new con */ + DLOG("Moving cons\n"); + Con *child; + while (!TAILQ_EMPTY(&(con->nodes_head))) { + child = TAILQ_FIRST(&(con->nodes_head)); + con_detach(child); + con_attach(child, new, true); + } + + /* 4: attach the new split container to the workspace */ + DLOG("Attaching new split to ws\n"); + con_attach(new, con, false); + + if (old_focused) + con_focus(old_focused); + + tree_flatten(croot); } - - /* 4: attach the new split container to the workspace */ - DLOG("Attaching new split to ws\n"); - con_attach(new, con, false); - - if (old_focused) - con_focus(old_focused); - - tree_flatten(croot); - return; } diff --git a/src/ipc.c b/src/ipc.c index 1c6de798..7dfbc871 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -231,6 +231,23 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { break; } + ystr("workspace_layout"); + switch (con->workspace_layout) { + case L_DEFAULT: + ystr("default"); + break; + case L_STACKED: + ystr("stacked"); + break; + case L_TABBED: + ystr("tabbed"); + break; + default: + DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout); + assert(false); + break; + } + ystr("last_split_layout"); switch (con->layout) { case L_SPLITV: diff --git a/src/load_layout.c b/src/load_layout.c index 795fb6d8..cce1a712 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -211,6 +211,17 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { json_node->layout = L_SPLITV; else LOG("Unhandled \"layout\": %s\n", buf); free(buf); + } else if (strcasecmp(last_key, "workspace_layout") == 0) { + char *buf = NULL; + sasprintf(&buf, "%.*s", (int)len, val); + if (strcasecmp(buf, "default") == 0) + json_node->workspace_layout = L_DEFAULT; + else if (strcasecmp(buf, "stacked") == 0) + json_node->workspace_layout = L_STACKED; + else if (strcasecmp(buf, "tabbed") == 0) + json_node->workspace_layout = L_TABBED; + else LOG("Unhandled \"workspace_layout\": %s\n", buf); + free(buf); } else if (strcasecmp(last_key, "last_split_layout") == 0) { char *buf = NULL; sasprintf(&buf, "%.*s", (int)len, val); diff --git a/src/workspace.c b/src/workspace.c index 1749959a..94efd47b 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -73,6 +73,7 @@ Con *workspace_get(const char *num, bool *created) { workspace->type = CT_WORKSPACE; FREE(workspace->name); workspace->name = sstrdup(num); + workspace->workspace_layout = config.default_layout; /* We set ->num to the number if this workspace’s name begins with a * positive number. Otherwise it’s a named ws and num will be -1. */ char *endptr = NULL; @@ -742,7 +743,7 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { Con *workspace_attach_to(Con *ws) { DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name); - if (config.default_layout == L_DEFAULT) { + if (ws->workspace_layout == L_DEFAULT) { DLOG("Default layout, just attaching it to the workspace itself.\n"); return ws; } @@ -754,7 +755,7 @@ Con *workspace_attach_to(Con *ws) { new->split = true; /* 2: set the requested layout on the split con */ - new->layout = config.default_layout; + new->layout = ws->workspace_layout; /* 4: attach the new split container to the workspace */ DLOG("Attaching new split %p to workspace %p\n", new, ws); diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 79447386..70008801 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -68,6 +68,7 @@ my $expected = { urgent => JSON::XS::false, border => 'normal', 'floating_nodes' => $ignore, + workspace_layout => 'default', }; # a shallow copy is sufficient, since we only ignore values at the root From 91d6fde7f5ba5a0899f8193ae23d05a15111339b Mon Sep 17 00:00:00 2001 From: Simon Elsbrock Date: Sun, 16 Sep 2012 22:55:37 +0200 Subject: [PATCH 193/200] hacking-howto: fix wrong anchor to #_tree_reply --- docs/hacking-howto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hacking-howto b/docs/hacking-howto index 7f2c35e6..8a246efc 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -77,7 +77,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#_get_tree_reply for documentation on that and an +http://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an example. == Files From c5db01e6f80480984b7944d7feaadc2f08f2e80c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:13:00 +0200 Subject: [PATCH 194/200] userguide: make moving cons/workspaces to other outputs its own section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frequent requests for this functionality on IRC made me aware that it’s not easily findable. --- docs/userguide | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/docs/userguide b/docs/userguide index e42a6acb..f92ef94a 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1390,20 +1390,13 @@ container to workspace next+, +move container to workspace prev+ to move a container to the next/previous workspace and +move container to workspace current+ (the last one makes sense only when used with criteria). +See <> for how to move a container/workspace to a different +RandR output. + [[back_and_forth]] To switch back to the previously focused workspace, use +workspace back_and_forth+. -To move a container to another xrandr output such as +LVDS1+ or +VGA1+, you can -use the +move container to output+ command followed by the name of the target -output. You may also use +left+, +right+, +up+, +down+ instead of the xrandr -output name to move to the next output in the specified direction. - -To move a whole workspace to another xrandr output such as +LVDS1+ or +VGA1+, -you can use the +move workspace to output+ command followed by the name of the -target output. You may also use +left+, +right+, +up+, +down+ instead of the -xrandr output name to move to the next output in the specified direction. - *Syntax*: ----------------------------------- workspace @@ -1465,7 +1458,7 @@ to switch to the workspace which begins with number 1, regardless of which name it has. This is useful in case you are changing the workspace’s name dynamically. -=== Renaming workspaces +==== Renaming workspaces You can rename workspaces. This might be useful to start with the default numbered workspaces, do your work, and rename the workspaces afterwards to @@ -1483,6 +1476,30 @@ i3-msg 'rename workspace 1 to "1: www"' i3-msg 'rename workspace "1: www" to "10: www"' ------------------------------------------------ +=== Moving containers/workspaces to RandR outputs + +[[move_to_outputs]] + +To move a container to another RandR output (addressed by names like +LVDS1+ or ++VGA1+) or to a RandR output identified by a specific direction (like +left+, ++right+, +up+ or +down+), there are two commands: + +*Syntax*: +-------------------------------------------------------- +move container to output <|> +move workspace to output <|> +-------------------------------------------------------- + +*Examples*: +-------------------------------------------------------- +# Move the current workspace to the next output +# (effectively toggles when you only have two outputs) +bindsym mod+x move workspace to output right + +# Put this window on the presentation output. +bindsym mod+x move container to output VGA1 +-------------------------------------------------------- + [[resizingconfig]] === Resizing containers/windows From 9febbe119a1d5891ac8b69a48254b7b1214f9cff Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:30:44 +0200 Subject: [PATCH 195/200] userguide: mention the FAQ --- docs/userguide | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide b/docs/userguide index f92ef94a..2214f016 100644 --- a/docs/userguide +++ b/docs/userguide @@ -4,8 +4,8 @@ Michael Stapelberg August 2012 This document contains all the information you need to configure and use the i3 -window manager. If it does not, please contact us on IRC (preferred) or post your -question(s) on the mailing list. +window manager. If it does not, please check http://faq.i3wm.org/ first, then +contact us on IRC (preferred) or post your question(s) on the mailing list. == Default keybindings From 672c965c563ecf8283c04008e186dbf0742479e7 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:46:42 +0200 Subject: [PATCH 196/200] release notes: update and final touches --- RELEASE-NOTES-4.3 | 65 +++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/RELEASE-NOTES-4.3 b/RELEASE-NOTES-4.3 index d828a0dc..ca77397a 100644 --- a/RELEASE-NOTES-4.3 +++ b/RELEASE-NOTES-4.3 @@ -3,40 +3,41 @@ │ Release notes for i3 v4.3 │ └──────────────────────────────┘ -This is the third release of the new major version of i3, v4.3. It is -considered stable. All users of i3 are strongly encouraged to upgrade. +This is the i3 v4.3. This version is considered stable. All users of i3 are +strongly encouraged to upgrade. One rather visible change is that commands which could not be parsed properly - will now spawn i3-nagbar. In case you used "bindsym $mod+x firefox" (and - forgot the "exec" keyword) or you made a typo in your config, you will now - notice that :). + will now spawn i3-nagbar. In case you used "bindsym $mod+x firefox" (and + forgot the "exec" keyword) or you made a typo in your config, you will now + notice that :). + We also made the orientation (horizontal/vertical) part of the layout mechanism: Before, we got the default layout and you could change orientation. Now, there are two new layouts "splitv" and "splith", which replace the default layout. The "split h" and "split v" commands continue to work as before, they split the current container and you will end up in a - split container with layout splith (after "split h") or splitv (after "split - v"). + split container with layout splith (after "split h") or splitv (after + "split v"). To change a splith container into a splitv container, use either "layout splitv" or "layout toggle split". The latter command is used in the - default config as mod+l (previously "layout default"). In case you have + default config as mod+l (formerly "layout default"). In case you have "layout default" in your config file, it is recommended to just replace it by "layout toggle split", which will work as "layout default" did before when pressing it once, but toggle between horizontal/vertical when pressing it repeatedly. - The rationale behind this commit is that it’s cleaner to have all + The rationale behind this change is that it’s cleaner to have all parameters that influence how windows are rendered in the layout itself - rather than having a special parameter in combination with only one - layout. This enables us to change existing split containers in all cases - without breaking existing features (see ticket #464). Also, users should - feel more confident about whether they are actually splitting or just - changing an existing split container now. + rather than having one special layout in combination with an additional + orientation. This enables us to change existing split containers in all + cases without breaking existing features (see ticket #464). Also, users + should feel more confident about whether they are actually splitting or + just changing an existing split container now. As a nice side-effect, this commit brings back the "layout toggle" - feature we once had in i3 version 3 (see the userguide). + feature we once had in i3 v3 (see the userguide). Another very important change is that we now support pango for rendering text. @@ -47,8 +48,8 @@ Another very important change is that we now support pango for rendering text. example) and to support right-to-left rendering (open http://www.ftpal.net/ for an example). Supporting users from all over the planet is important, and as such I would strongly advise distribution packagers to leave pango support - in. In case you are working on a very low-spec embedded device, it is easy - enough to disable pango support, see common.mk. + enabled. In case you are working on a very low-spec embedded device, it is + easy enough to disable pango support, see common.mk. Also, the 'layout' command now always works on the parent split container. This @@ -62,13 +63,25 @@ Also, the 'layout' command now always works on the parent split container. This for_window [class="XTerm"] split v, layout tabbed + +Furthermore, we decided to entirely ignore resize increment size hints for + tiling windows. These are set by terminal emulators (such as urxvt, + gnome-terminal, …) and specify that the window may only be resized in + multiples of the specified size. All terminal emulators cope with the window + manager ignoring these hints and by doing so, they can decide what to do with + the lost space (that is, pseudo-transparency now works without black bars in + urxvt). + ┌────────────────────────────┐ │ Changes in v4.3 │ └────────────────────────────┘ + • docs: there now is documentation about lib::i3test and lib::i3test::Test, + the main Perl modules used by our testsuite. • docs/refcard: update for v4 • docs/userguide: clarify the default for focus_follows_mouse and new_window • docs/userguide: add section about implicit containers + • docs/userguide: give 'move to output' its own section • docs/ipc: document the 'window' field in the GET_TREE reply • docs/ipc: update links to ipc libraries • docs/ipc: make the reply sections consistent @@ -112,6 +125,13 @@ Also, the 'layout' command now always works on the parent split container. This • 'move workspace number n' will now create the workspace if it doesn’t exist • Accept slashes in RandR output names • Keep startup-notification sequences around for 30s after completion + • Introduce bindsym --release, which will trigger the binding not on the + KeyPress event, but on the KeyRelease event (useful for import(1) or + xdotool(1)). + • The signalhandler does not offer you to exit i3 anymore. Instead, there is + 'b' for writing a backtrace to a file in /tmp (if gdb is installed) + • Remove support for resize increment hints for tiling windows + • Exit fullscreen mode when 'scratchpad show' is executed while in fullscreen ┌────────────────────────────┐ │ Bugfixes │ @@ -149,6 +169,7 @@ Also, the 'layout' command now always works on the parent split container. This issue with gnome-terminal and xfce’s terminal) • Fix flickering with 1pixel border tabbed layouts • Use _exit() instead of exit() when i3 utility programs cannot be executed + • Don’t focus the wrong workspace when moving to scratchpad ┌────────────────────────────┐ │ Thanks! │ @@ -157,9 +178,9 @@ Also, the 'layout' command now always works on the parent split container. This Thanks for testing, bugfixes, discussions and everything I forgot go out to: aksr, Axel Wagner, darkraven, David Coppa, eeemsi, Felicitus, Fernando Tarlá - Cardoso Lemos, Iakov Davydov, jh, Joel Stemmer, Julius Plenz, Marcel Hellwig, - Marcus, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel Löbl, Philipp - Middendorf, prg, Quentin Glidic, Sebastian Ullrich, somelauw, stfn, tucos, - TunnelWicht, Valentin Haenel + Cardoso Lemos, Iakov Davydov, jh, Joel Stemmer, Julius Plenz, loblik, Marcel + Hellwig, Marcus, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel + Löbl, Philipp Middendorf, prg, Quentin Glidic, Sebastian Ullrich, Simon + Elsbrock, somelauw, stfn, tucos, TunnelWicht, Valentin Haenel --- Michael Stapelberg, 2012-09-06 +-- Michael Stapelberg, 2012-09-19 From 99e91c804989990d977ee69c5ff4fcf6964c9f2d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:52:56 +0200 Subject: [PATCH 197/200] =?UTF-8?q?--help:=20note=20that=20nVidia=E2=80=99?= =?UTF-8?q?s=20driver=20supports=20RandR=20from=20302.17=20on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 7936e758..8bae9957 100644 --- a/src/main.c +++ b/src/main.c @@ -391,7 +391,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "\t--force-xinerama\n" "\tUse Xinerama instead of RandR.\n" "\tThis option should only be used if you are stuck with the\n" - "\tnvidia closed source driver which does not support RandR.\n"); + "\told nVidia closed source driver (older than 302.17), which does\n" + "\tnot support RandR.\n"); fprintf(stderr, "\n"); fprintf(stderr, "\t--get-socketpath\n" "\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n"); From 156bd24f23e6bf6439d986782a4066521d9b13b0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:53:17 +0200 Subject: [PATCH 198/200] man: update i3(1) --- man/i3.man | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/man/i3.man b/man/i3.man index 096a359b..4358463b 100644 --- a/man/i3.man +++ b/man/i3.man @@ -1,7 +1,7 @@ i3(1) ===== Michael Stapelberg -v4.2, August 2012 +v4.3, September 2012 == NAME @@ -32,6 +32,18 @@ Display version number (and date of the last commit). -V:: Be verbose. +--force-xinerama:: +Use Xinerama instead of RandR. This option should only be used if you are stuck +with the old nVidia closed source driver (older than 302.17) which does not +support RandR. + +--get-socketpath:: +Retrieve the i3 IPC socket path from X11, print it, then exit. + +--shmlog-size :: +Limits the size of the i3 SHM log to bytes. Setting this to 0 disables +SHM logging entirely. The default is 0 bytes. + == DESCRIPTION === INTRODUCTION @@ -316,7 +328,7 @@ and the "how to hack" guide. If you are building from source, run: You can also access these documents online at http://i3wm.org/ -i3-input(1), i3-msg(1), i3-wsbar(1), i3-nagbar(1), i3-config-wizard(1), +i3-input(1), i3-msg(1), i3bar(1), i3-nagbar(1), i3-config-wizard(1), i3-migrate-config-to-v4(1) == AUTHOR From c808a39fb2b02ba2c32be99edc818765cfd259be Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 17:53:27 +0200 Subject: [PATCH 199/200] man: set version to 4.3 --- man/asciidoc.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/asciidoc.conf b/man/asciidoc.conf index 79cf2b41..32d2186f 100644 --- a/man/asciidoc.conf +++ b/man/asciidoc.conf @@ -7,7 +7,7 @@ template::[header-declarations] {mantitle} {manvolnum} i3 -4.1.2 +4.3 i3 Manual From b9fde552d0c4605cadca96a23642366b032d08de Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 Sep 2012 18:05:51 +0200 Subject: [PATCH 200/200] fix make clean targets --- i3-config-wizard/i3-config-wizard.mk | 2 +- libi3/libi3.mk | 2 +- src/i3.mk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 1598cfed..3847786e 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -34,4 +34,4 @@ install-i3-config-wizard: i3-config-wizard/i3-config-wizard clean-i3-config-wizard: echo "[i3-config-wizard] Clean" - rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.{output,dot} + rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.{output,dot,tab.h,y.o} diff --git a/libi3/libi3.mk b/libi3/libi3.mk index d99bacf4..70521073 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -18,4 +18,4 @@ libi3.a: $(libi3_OBJECTS) clean-libi3: echo "[libi3] Clean" - rm -f $(libi3_OBJECTS) libi3.a + rm -f $(libi3_OBJECTS) libi3/libi3.a diff --git a/src/i3.mk b/src/i3.mk index a93cc4c4..78e19890 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -82,4 +82,4 @@ install-i3: i3 clean-i3: echo "[i3] Clean" - rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot} src/cmdparse.* + rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot,tab.h,y.o} src/cmdparse.*