Con *current_ws = con_get_workspace(old_focus);
const bool focused_within_first = (first == old_focus || con_has_parent(old_focus, first));
const bool focused_within_second = (second == old_focus || con_has_parent(old_focus, second));
+ fullscreen_mode_t first_fullscreen_mode = first->fullscreen_mode;
+ fullscreen_mode_t second_fullscreen_mode = second->fullscreen_mode;
- if (!con_fullscreen_permits_focusing(first_ws)) {
- DLOG("Cannot swap because target workspace \"%s\" is obscured.\n", first_ws->name);
- return false;
+ if (first_fullscreen_mode != CF_NONE) {
+ con_disable_fullscreen(first);
}
-
- if (!con_fullscreen_permits_focusing(second_ws)) {
- DLOG("Cannot swap because target workspace \"%s\" is obscured.\n", second_ws->name);
- return false;
+ if (second_fullscreen_mode != CF_NONE) {
+ con_disable_fullscreen(second);
}
double first_percent = first->percent;
second->percent = first_percent;
fake->percent = 0.0;
+ SWAP(first_fullscreen_mode, second_fullscreen_mode, fullscreen_mode_t);
+
swap_end:
+ /* The two windows exchange their original fullscreen status */
+ if (first_fullscreen_mode != CF_NONE) {
+ con_enable_fullscreen(first, first_fullscreen_mode);
+ }
+ if (second_fullscreen_mode != CF_NONE) {
+ con_enable_fullscreen(second, second_fullscreen_mode);
+ }
+
/* We don't actually need this since percentages-wise we haven't changed
* anything, but we'll better be safe than sorry and just make sure as we'd
* otherwise crash i3. */
EOT
my ($ws, $ws1, $ws2, $ws3);
-my ($nodes, $expected_focus, $A, $B, $F);
+my ($node, $nodes, $expected_focus, $A, $B, $F);
my ($result);
+my @fullscreen_permutations = ([], ["A"], ["B"], ["A", "B"]);
my @urgent;
+sub fullscreen_windows {
+ my $ws = shift if @_;
+
+ scalar grep { $_->{fullscreen_mode} != 0 } @{get_ws_content($ws)}
+}
+
###############################################################################
# Invalid con_id should not crash i3
# See issue #2895.
# | Y | B | Focus Stacks:
# +---+---+ H2: B, Y
###############################################################################
+for my $fullscreen (@fullscreen_permutations){
+ $ws1 = fresh_workspace;
+ $A = open_window(wm_class => 'mark_A');
+ $expected_focus = get_focused($ws1);
+ open_window;
+ cmd 'focus left';
+
+ $ws2 = fresh_workspace;
+ open_window;
+ $B = open_window(wm_class => 'mark_B');
+
+ my $A_fullscreen = "A" ~~ @$fullscreen || 0;
+ my $B_fullscreen = "B" ~~ @$fullscreen || 0;
+ $A->fullscreen($A_fullscreen);
+ $B->fullscreen($B_fullscreen);
+ sync_with_i3;
+
+ cmd '[con_mark=B] swap container with mark A';
+
+ $nodes = get_ws_content($ws1);
+ $node = $nodes->[0];
+ is($node->{window}, $B->{id}, 'B is on ws1:left');
+ is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1');
+ is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode');
+
+ $nodes = get_ws_content($ws2);
+ $node = $nodes->[1];
+ is($node->{window}, $A->{id}, 'A is on ws2:right');
+ is(get_focused($ws2), $expected_focus, 'A is focused');
+ is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2');
+ is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode');
+
+ kill_all_windows;
+}
+
+###############################################################################
+# Swap a non-fullscreen window with a fullscreen one in different workspaces.
+# Layout: O1[ W1[ H1 ] W2[ B ] ]
+#
+# +---+---+ Layout: H1[ A F ]
+# | A | F | Focus Stacks:
+# +---+---+ H1: F, A
+#
+# +---+---+
+# | B |
+# +---+---+
+###############################################################################
$ws1 = fresh_workspace;
+
$A = open_window(wm_class => 'mark_A');
+$F = open_window();
+$F->fullscreen(1);
$expected_focus = get_focused($ws1);
-open_window;
-cmd 'focus left';
$ws2 = fresh_workspace;
-open_window;
$B = open_window(wm_class => 'mark_B');
+$B->fullscreen(1);
cmd '[con_mark=B] swap container with mark A';
$nodes = get_ws_content($ws1);
is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left');
+is(fullscreen_windows($ws1), 1, 'F still fullscreen in ws1');
+is(get_focused($ws1), $expected_focus, 'F is still focused');
$nodes = get_ws_content($ws2);
-is($nodes->[1]->{window}, $A->{id}, 'A is on ws2:right');
-is(get_focused($ws2), $expected_focus, 'A is focused');
+is($nodes->[0]->{window}, $A->{id}, 'A is on ws1');
-kill_all_windows;
+###############################################################################
+# Try a more exotic layout with fullscreen containers.
+# A and F are fullscreened as a stack of two vertical containers before the
+# swap is performed.
+# A is swapped with fullscreened window B which is in another workspace.
+#
+# +---+---+ Layout: H1[ X V1[ A F ] ]
+# | | A | Focus Stacks:
+# | X +---+ H1: V1, X
+# | | F | V1: F, A
+# +---+---+
+###############################################################################
+$ws1 = fresh_workspace;
+
+open_window;
+$A = open_window(wm_class => 'mark_A');
+cmd "split v";
+open_window;
+cmd "focus parent";
+cmd "fullscreen enable";
+$F = fullscreen_windows($ws1);
+$expected_focus = get_focused($ws1);
+
+$ws2 = fresh_workspace;
+$B = open_window(wm_class => 'mark_B');
+$B->fullscreen(1);
+
+cmd '[con_mark=B] swap container with mark A';
+
+sync_with_i3;
+does_i3_live;
+
+$nodes = get_ws_content($ws1);
+is($nodes->[1]->{nodes}->[0]->{window}, $B->{id}, 'B is on top right in ws1');
+is(get_focused($ws1), $expected_focus, 'The container of the stacked windows remains focused in ws1');
+is(fullscreen_windows($ws1), $F, 'Same amount of fullscreen windows in ws1');
+
+$nodes = get_ws_content($ws2);
+is($nodes->[0]->{window}, $A->{id}, 'A is on ws2');
+is(fullscreen_windows($ws2), 1, 'A is in fullscreen mode');
###############################################################################
# Swap two non-focused containers within the same workspace.
# | F |
# +---+
###############################################################################
-$ws1 = fresh_workspace;
-$A = open_window(wm_class => 'mark_A');
+for my $fullscreen (@fullscreen_permutations){
+ $ws1 = fresh_workspace;
+ $A = open_window(wm_class => 'mark_A');
-$ws2 = fresh_workspace;
-$B = open_window(wm_class => 'mark_B');
+ $ws2 = fresh_workspace;
+ $B = open_window(wm_class => 'mark_B');
-$ws3 = fresh_workspace;
-open_window;
-$expected_focus = get_focused($ws3);
+ $ws3 = fresh_workspace;
+ open_window;
+ $expected_focus = get_focused($ws3);
-cmd '[con_mark=B] swap container with mark A';
+ my $A_fullscreen = "A" ~~ @$fullscreen || 0;
+ my $B_fullscreen = "B" ~~ @$fullscreen || 0;
+ $A->fullscreen($A_fullscreen);
+ $B->fullscreen($B_fullscreen);
+ sync_with_i3;
-$nodes = get_ws_content($ws1);
-is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
+ cmd '[con_mark=B] swap container with mark A';
-$nodes = get_ws_content($ws2);
-is($nodes->[0]->{window}, $A->{id}, 'A is on the second workspace');
+ $nodes = get_ws_content($ws1);
+ $node = $nodes->[0];
+ is($node->{window}, $B->{id}, 'B is on the first workspace');
+ is(fullscreen_windows($ws1), $A_fullscreen, 'amount of fullscreen windows in ws1');
+ is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode');
-is(get_focused($ws3), $expected_focus, 'F is still focused');
+ $nodes = get_ws_content($ws2);
+ $node = $nodes->[0];
+ is($node->{window}, $A->{id}, 'A is on the second workspace');
+ is(fullscreen_windows($ws2), $B_fullscreen, 'amount of fullscreen windows in ws2');
+ is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode');
-kill_all_windows;
+ is(get_focused($ws3), $expected_focus, 'F is still focused');
+
+ kill_all_windows;
+}
###############################################################################
# Swap two non-focused containers with one being on a different workspace.