void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates,
bool dont_warp, bool ignore_focus);
+/**
+ * Moves the given container to the currently focused container on the
+ * visible workspace on the given output.
+ *
+ */
+void con_move_to_output(Con *con, Output *output);
+
/**
* Moves the given container to the given mark.
*
*/
Output *get_output_containing(unsigned int x, unsigned int y);
+/**
+ * Returns the active output which spans exactly the area specified by
+ * rect or NULL if there is no output like this.
+ *
+ */
+Output *get_output_with_dimensions(Rect rect);
+
/*
* In contained_by_output, we check if any active output contains part of the container.
* We do this by checking if the output rect is intersected by the Rect.
_con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus);
}
+/*
+ * Moves the given container to the currently focused container on the
+ * visible workspace on the given output.
+ *
+ */
+void con_move_to_output(Con *con, Output *output) {
+ Con *ws = NULL;
+ GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
+ assert(ws != NULL);
+ DLOG("Moving con %p to output %s\n", con, output->name);
+ con_move_to_workspace(con, ws, false, false, false);
+}
+
/*
* Returns the orientation of the given container (for stacked containers,
* vertical orientation is used regardless of the actual orientation of the
if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) {
/* If this window is already fullscreen (after restarting!), skip
* toggling fullscreen, that would drop it out of fullscreen mode. */
- if (fs != nc)
+ if (fs != nc) {
+ Output *output = get_output_with_dimensions((Rect){geom->x, geom->y, geom->width, geom->height});
+ /* If the requested window geometry spans the whole area
+ * of an output, move the window to that output. This is
+ * needed e.g. for LibreOffice Impress multi-monitor
+ * presentations to work out of the box. */
+ if (output != NULL)
+ con_move_to_output(nc, output);
con_toggle_fullscreen(nc, CF_OUTPUT);
+ }
fs = NULL;
}
return NULL;
}
+/*
+ * Returns the active output which spans exactly the area specified by
+ * rect or NULL if there is no output like this.
+ *
+ */
+Output *get_output_with_dimensions(Rect rect) {
+ Output *output;
+ TAILQ_FOREACH(output, &outputs, outputs) {
+ if (!output->active)
+ continue;
+ DLOG("comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
+ rect.x, rect.y, rect.width, rect.height,
+ output->rect.x, output->rect.y, output->rect.width, output->rect.height);
+ if (rect.x == output->rect.x && rect.width == output->rect.width &&
+ rect.y == output->rect.y && rect.height == output->rect.height)
+ return output;
+ }
+
+ return NULL;
+}
+
/*
* In contained_by_output, we check if any active output contains part of the container.
* We do this by checking if the output rect is intersected by the Rect.
--- /dev/null
+#!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 fullscreen windows appear on the output indicated by
+# their geometry
+use i3test i3_autostart => 0;
+use List::Util qw(first);
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+
+my $pid = launch_with_config($config);
+
+# Helper functions
+sub fullscreen($) {
+ my ($window) = @_;
+ $window->fullscreen(1);
+}
+
+sub find_window {
+ my ($nodes, $id) = @_;
+
+ foreach (@{$nodes}) {
+ return $_ if ($_->{window} // 0) == $id;
+ my $node = find_window($_->{nodes}, $id);
+ return $node if $node;
+ };
+ return undef;
+}
+
+# Create two fullscreen windows, each on different output
+my $orig_rect1 = X11::XCB::Rect->new(x => 0, y => 0, width => 1024, height => 768);
+my $orig_rect2 = X11::XCB::Rect->new(x => 1024, y => 0, width => 1024, height => 768);
+
+my $win_on_first_output = open_window(rect => $orig_rect1,
+ before_map => \&fullscreen);
+
+my $win_on_second_output = open_window(rect => $orig_rect2,
+ before_map => \&fullscreen);
+
+sync_with_i3;
+
+# Check that the windows are on the correct output
+is_deeply(scalar $win_on_first_output->rect, $orig_rect1, "first window spans the first output");
+is_deeply(scalar $win_on_second_output->rect, $orig_rect2, "second window spans the sencond output");
+
+# Check that both windows remained fullscreen
+my $tree = i3(get_socket_path())->get_tree->recv;
+
+my $node1 = find_window($tree->{nodes}, $win_on_first_output->{id});
+my $node2 = find_window($tree->{nodes}, $win_on_second_output->{id});
+
+is($node1->{fullscreen_mode}, 1, "first window is fullscreen");
+is($node2->{fullscreen_mode}, 1, "second window is fullscreen");
+
+exit_gracefully($pid);
+
+done_testing;