#!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 swap command. # Ticket: #917 use i3test i3_config => <{fullscreen_mode} != 0 } @{get_ws_content($ws)} } ############################################################################### # Invalid con_id should not crash i3 # See issue #2895. ############################################################################### $ws = fresh_workspace; open_window; cmd "swap container with con_id 1"; does_i3_live; kill_all_windows; ############################################################################### # Swap 2 windows in different workspaces using con_id ############################################################################### $ws = fresh_workspace; open_window; $A = get_focused($ws); $ws = fresh_workspace; open_window; cmd "swap container with con_id $A"; is(get_focused($ws), $A, 'A is now focused'); kill_all_windows; ############################################################################### # Swap two containers next to each other. # Focus should stay on B because both windows are on the focused workspace. # The focused container is B. # # +---+---+ Layout: H1[ A B ] # | A | B | Focus Stacks: # +---+---+ H1: B, A ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); $expected_focus = get_focused($ws); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); is($nodes->[0]->{window}, $B->{id}, 'B is on the left'); is($nodes->[1]->{window}, $A->{id}, 'A is on the right'); is(get_focused($ws), $expected_focus, 'B is still focused'); kill_all_windows; ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is A. # The focused container is B. # # +---+---+ Layout: H1[ V1[ A Y ] V2[ X B ] ] # | A | X | Focus Stacks: # +---+---+ H1: V2, V1 # | Y | B | V1: A, Y # +---+---+ V2: B, X ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); cmd 'split v'; open_window; cmd 'move up, focus left'; cmd 'split v'; open_window; cmd 'focus up, focus right, focus down'; $expected_focus = get_focused($ws); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); kill_all_windows; ############################################################################### # Swap two containers with different parents. # In this test, the focus head of the left v-split container is _not_ A. # The focused container is B. # # +---+---+ Layout: H1[ V1[ A Y ] V2[ X B ] ] # | A | X | Focus Stacks: # +---+---+ H1: V2, V1 # | Y | B | V1: Y, A # +---+---+ V2: B, X ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); cmd 'split v'; open_window; cmd 'move up, focus left'; cmd 'split v'; open_window; cmd 'focus right, focus down'; $expected_focus = get_focused($ws); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'B is still focused'); kill_all_windows; ############################################################################### # Swap two containers with one being on a different workspace. # The focused container is B. # # Layout: O1[ W1[ H1 ] W2[ H2 ] ] # Focus Stacks: # O1: W2, W1 # # +---+---+ Layout: H1[ A X ] # | A | X | Focus Stacks: # +---+---+ H1: A, X # # +---+---+ Layout: H2[ Y, B ] # | Y | B | Focus Stacks: # +---+---+ H2: B, Y ############################################################################### for my $fullscreen (@fullscreen_permutations){ $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $expected_focus = get_focused($ws1); open_window; cmd 'focus left'; $ws2 = fresh_workspace; open_window; $B = open_window(wm_class => 'mark_B'); my $A_fullscreen = "A" ~~ @$fullscreen || 0; my $B_fullscreen = "B" ~~ @$fullscreen || 0; $A->fullscreen($A_fullscreen); $B->fullscreen($B_fullscreen); sync_with_i3; cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws1); $node = $nodes->[0]; is($node->{window}, $B->{id}, 'B is on ws1:left'); is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1'); is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode'); $nodes = get_ws_content($ws2); $node = $nodes->[1]; is($node->{window}, $A->{id}, 'A is on ws2:right'); is(get_focused($ws2), $expected_focus, 'A is focused'); is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2'); is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode'); kill_all_windows; } ############################################################################### # Swap a non-fullscreen window with a fullscreen one in different workspaces. # Layout: O1[ W1[ H1 ] W2[ B ] ] # # +---+---+ Layout: H1[ A F ] # | A | F | Focus Stacks: # +---+---+ H1: F, A # # +---+---+ # | B | # +---+---+ ############################################################################### $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $F = open_window(); $F->fullscreen(1); $expected_focus = get_focused($ws1); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); $B->fullscreen(1); sync_with_i3; cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws1); is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left'); is(fullscreen_windows($ws1), 1, 'F still fullscreen in ws1'); is(get_focused($ws1), $expected_focus, 'F is still focused'); $nodes = get_ws_content($ws2); is($nodes->[0]->{window}, $A->{id}, 'A is on ws1'); ############################################################################### # Try a more exotic layout with fullscreen containers. # A and F are fullscreened as a stack of two vertical containers before the # swap is performed. # A is swapped with fullscreened window B which is in another workspace. # # +---+---+ Layout: H1[ X V1[ A F ] ] # | | A | Focus Stacks: # | X +---+ H1: V1, X # | | F | V1: F, A # +---+---+ ############################################################################### $ws1 = fresh_workspace; open_window; $A = open_window(wm_class => 'mark_A'); cmd "split v"; open_window; cmd "focus parent"; cmd "fullscreen enable"; $F = fullscreen_windows($ws1); $expected_focus = get_focused($ws1); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); $B->fullscreen(1); sync_with_i3; cmd '[con_mark=B] swap container with mark A'; sync_with_i3; does_i3_live; $nodes = get_ws_content($ws1); is($nodes->[1]->{nodes}->[0]->{window}, $B->{id}, 'B is on top right in ws1'); is(get_focused($ws1), $expected_focus, 'The container of the stacked windows remains focused in ws1'); is(fullscreen_windows($ws1), $F, 'Same amount of fullscreen windows in ws1'); $nodes = get_ws_content($ws2); is($nodes->[0]->{window}, $A->{id}, 'A is on ws2'); is(fullscreen_windows($ws2), 1, 'A is in fullscreen mode'); ############################################################################### # Swap two non-focused containers within the same workspace. # # +---+---+ Layout: H1[ V1[ A X ] V2[ F B ] ] # | A | F | Focus Stacks: # +---+---+ H1: V2, V1 # | X | B | V1: A, X # +---+---+ V2: F, B ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); cmd 'split v'; open_window; cmd 'move up, focus left'; cmd 'split v'; open_window; cmd 'focus up, focus right'; $expected_focus = get_focused($ws); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left'); is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right'); is(get_focused($ws), $expected_focus, 'F is still focused'); kill_all_windows; ############################################################################### # Swap two non-focused containers which are both on different workspaces. # # Layout: O1[ W1[ A ] W2[ B ] W3[ F ] ] # Focus Stacks: # O1: W3, W2, W1 # # +---+ # | A | # +---+ # # +---+ # | B | # +---+ # # +---+ # | F | # +---+ ############################################################################### for my $fullscreen (@fullscreen_permutations){ $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); $ws3 = fresh_workspace; open_window; $expected_focus = get_focused($ws3); my $A_fullscreen = "A" ~~ @$fullscreen || 0; my $B_fullscreen = "B" ~~ @$fullscreen || 0; $A->fullscreen($A_fullscreen); $B->fullscreen($B_fullscreen); sync_with_i3; cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws1); $node = $nodes->[0]; is($node->{window}, $B->{id}, 'B is on the first workspace'); is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1'); is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode'); $nodes = get_ws_content($ws2); $node = $nodes->[0]; is($node->{window}, $A->{id}, 'A is on the second workspace'); is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2'); is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode'); is(get_focused($ws3), $expected_focus, 'F is still focused'); kill_all_windows; } ############################################################################### # Swap two non-focused containers with one being on a different workspace. # # Layout: O1[ W1[ A ] W2[ H2 ] ] # Focus Stacks: # O1: W2, W1 # # +---+ # | A | # +---+ # # +---+---+ Layout: H2[ B, F ] # | B | F | Focus Stacks: # +---+---+ H2: F, B ############################################################################### $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); open_window; $expected_focus = get_focused($ws2); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws1); is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace'); $nodes = get_ws_content($ws2); is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace'); is(get_focused($ws2), $expected_focus, 'F is still focused'); kill_all_windows; ############################################################################### # 1. A container cannot be swapped with its parent. # 2. A container cannot be swapped with one of its children. # # ↓A↓ # +---+---+ Layout: H1[ X V1[ Y B ] ] # | | Y | (with A := V1) # | X +---+ # | | B | # +---+---+ ############################################################################### $ws = fresh_workspace; open_window; open_window; cmd 'split v'; $B = open_window(wm_class => 'mark_B'); cmd 'focus parent, mark A, focus child'; $result = cmd '[con_mark=B] swap container with mark A'; is($result->[0]->{success}, 0, 'B cannot be swappd with its parent'); $result = cmd '[con_mark=A] swap container with mark B'; is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children'); kill_all_windows; ############################################################################### # Swapping two containers preserves the geometry of the container they are # being swapped with. # # Before: # +---+-------+ # | A | B | # +---+-------+ # # After: # +---+-------+ # | B | A | # +---+-------+ ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); cmd 'resize grow width 0 or 25 ppt'; # sanity checks $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{percent}, 0.25, 'A has 25% width'); cmp_float($nodes->[1]->{percent}, 0.75, 'B has 75% width'); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width'); cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width'); kill_all_windows; ############################################################################### # Swapping containers not sharing the same parent preserves the geometry of # the container they are swapped with. # # Before: # +---+-----+ # | A | | # +---+ B | # | | | # | Y +-----+ # | | X | # +---+-----+ # # After: # +---+-----+ # | B | | # +---+ A | # | | | # | Y +-----+ # | | X | # +---+-----+ ############################################################################### $ws = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $B = open_window(wm_class => 'mark_B'); cmd 'split v'; open_window; cmd 'focus up, resize grow height 0 or 25 ppt'; cmd 'focus left, split v'; open_window; cmd 'resize grow height 0 or 25 ppt'; # sanity checks $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'A has 25% height'); cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'B has 75% height'); cmd '[con_mark=B] swap container with mark A'; $nodes = get_ws_content($ws); cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height'); cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height'); kill_all_windows; ############################################################################### # Swapping containers moves the urgency hint correctly. ############################################################################### $ws1 = fresh_workspace; $A = open_window(wm_class => 'mark_A'); $ws2 = fresh_workspace; $B = open_window(wm_class => 'mark_B'); open_window; $B->add_hint('urgency'); sync_with_i3; cmd '[con_mark=B] swap container with mark A'; @urgent = grep { $_->{urgent} } @{get_ws_content($ws1)}; is(@urgent, 1, 'B is marked urgent'); is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent'); @urgent = grep { $_->{urgent} } @{get_ws_content($ws2)}; is(@urgent, 0, 'A is not marked urgent'); is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent'); kill_all_windows; ############################################################################### done_testing;