#include "scratchpad.h"
#include "commands.h"
#include "commands_parser.h"
+#include "fake_outputs.h"
#endif
* is fetched once and never updated. */
bool force_xinerama;
+ /** Overwrites output detection (for testing), see src/fake_outputs.c */
+ char *fake_outputs;
+
/** Automatic workspace back and forth switching. If this is set, a
* switch to the currently active workspace will switch to the
* previously focused one instead, making it possible to fast toggle
--- /dev/null
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * Faking outputs is useful in pathological situations (like network X servers
+ * which don’t support multi-monitor in a useful way) and for our testsuite.
+ *
+ */
+#ifndef _FAKE_OUTPUTS_H
+#define _FAKE_OUTPUTS_H
+
+/**
+ * Creates outputs according to the given specification.
+ * The specification must be in the format wxh+x+y, for example 1024x768+0+0,
+ * with multiple outputs separated by commas:
+ * 1900x1200+0+0,1280x1024+1900+0
+ *
+ */
+void fake_outputs_init(const char *output_spec);
+
+#endif
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; }
force_xinerama { return TOK_FORCE_XINERAMA; }
+fake_outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
+fake-outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
workspace_auto_back_and_forth { return TOK_WORKSPACE_AUTO_BAF; }
workspace_bar { return TOKWORKSPACEBAR; }
popup_during_fullscreen { return TOK_POPUP_DURING_FULLSCREEN; }
%token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse"
%token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping"
%token TOK_FORCE_XINERAMA "force_xinerama"
+%token TOK_FAKE_OUTPUTS "fake_outputs"
%token TOK_WORKSPACE_AUTO_BAF "workspace_auto_back_and_forth"
%token TOKWORKSPACEBAR "workspace_bar"
%token TOK_DEFAULT "default"
| focus_follows_mouse
| force_focus_wrapping
| force_xinerama
+ | fake_outputs
| workspace_back_and_forth
| workspace_bar
| workspace
}
;
+fake_outputs:
+ TOK_FAKE_OUTPUTS STR
+ {
+ DLOG("fake outputs = %s\n", $2);
+ config.fake_outputs = $2;
+ }
+ ;
+
workspace_back_and_forth:
TOK_WORKSPACE_AUTO_BAF bool
{
--- /dev/null
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * Faking outputs is useful in pathological situations (like network X servers
+ * which don’t support multi-monitor in a useful way) and for our testsuite.
+ *
+ */
+#include "all.h"
+
+static int num_screens;
+
+/*
+ * Looks in outputs for the Output whose start coordinates are x, y
+ *
+ */
+static Output *get_screen_at(int x, int y) {
+ Output *output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->rect.x == x && output->rect.y == y)
+ return output;
+
+ return NULL;
+}
+
+/*
+ * Creates outputs according to the given specification.
+ * The specification must be in the format wxh+x+y, for example 1024x768+0+0,
+ * with multiple outputs separated by commas:
+ * 1900x1200+0+0,1280x1024+1900+0
+ *
+ */
+void fake_outputs_init(const char *output_spec) {
+ char useless_buffer[1024];
+ const char *walk = output_spec;
+ unsigned int x, y, width, height;
+ while (sscanf(walk, "%ux%u+%u+%u", &width, &height, &x, &y) == 4) {
+ DLOG("Parsed output as width = %u, height = %u at (%u, %u)\n",
+ width, height, x, y);
+ Output *new_output = get_screen_at(x, y);
+ if (new_output != NULL) {
+ DLOG("Re-used old output %p\n", new_output);
+ /* This screen already exists. We use the littlest screen so that the user
+ can always see the complete workspace */
+ new_output->rect.width = min(new_output->rect.width, width);
+ new_output->rect.height = min(new_output->rect.height, height);
+ } else {
+ new_output = scalloc(sizeof(Output));
+ sasprintf(&(new_output->name), "fake-%d", num_screens);
+ DLOG("Created new fake output %s (%p)\n", new_output->name, new_output);
+ new_output->active = true;
+ new_output->rect.x = x;
+ new_output->rect.y = y;
+ new_output->rect.width = width;
+ new_output->rect.height = height;
+ /* We always treat the screen at 0x0 as the primary screen */
+ if (new_output->rect.x == 0 && new_output->rect.y == 0)
+ TAILQ_INSERT_HEAD(&outputs, new_output, outputs);
+ else TAILQ_INSERT_TAIL(&outputs, new_output, outputs);
+ output_init_con(new_output);
+ init_ws_for_output(new_output, output_get_content(new_output->con));
+ num_screens++;
+ }
+
+ /* Figure out how long the input was to skip it */
+ walk += sprintf(useless_buffer, "%ux%u+%u+%u", width, height, x, y) + 1;
+ }
+
+ if (num_screens == 0) {
+ ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
+ exit(0);
+ }
+}
char *layout_path = NULL;
bool delete_layout_path = false;
bool force_xinerama = false;
+ char *fake_outputs = NULL;
bool disable_signalhandler = false;
static struct option long_options[] = {
{"no-autostart", no_argument, 0, 'a'},
{"shmlog_size", required_argument, 0, 0},
{"get-socketpath", no_argument, 0, 0},
{"get_socketpath", no_argument, 0, 0},
+ {"fake_outputs", required_argument, 0, 0},
+ {"fake-outputs", required_argument, 0, 0},
{0, 0, 0, 0}
};
int option_index = 0, opt;
layout_path = sstrdup(optarg);
delete_layout_path = true;
break;
+ } else if (strcmp(long_options[option_index].name, "fake-outputs") == 0 ||
+ strcmp(long_options[option_index].name, "fake_outputs") == 0) {
+ LOG("Initializing fake outputs: %s\n", optarg);
+ fake_outputs = sstrdup(optarg);
+ break;
}
/* fall-through */
default:
free(greply);
- /* Force Xinerama (for drivers which don't support RandR yet, esp. the
- * nVidia binary graphics driver), when specified either in the config
- * file or on command-line */
- if (force_xinerama || config.force_xinerama) {
+ /* Setup fake outputs for testing */
+ if (fake_outputs == NULL && config.fake_outputs != NULL)
+ fake_outputs = config.fake_outputs;
+
+ if (fake_outputs != NULL) {
+ fake_outputs_init(fake_outputs);
+ FREE(fake_outputs);
+ config.fake_outputs = NULL;
+ } else if (force_xinerama || config.force_xinerama) {
+ /* Force Xinerama (for drivers which don't support RandR yet, esp. the
+ * nVidia binary graphics driver), when specified either in the config
+ * file or on command-line */
xinerama_init();
} else {
DLOG("Checking for XRandR...\n");
my $numtests = scalar @testfiles;
-# When the user specifies displays, we don’t run multi-monitor tests at all
-# (because we don’t know which displaynumber is the X-Server with multiple
-# monitors).
-my $multidpy = undef;
-
# No displays specified, let’s start some Xdummy instances.
if (@displays == 0) {
- my $dpyref;
- ($dpyref, $multidpy) = start_xdummy($parallel, $numtests);
- @displays = @$dpyref;
+ @displays = start_xdummy($parallel, $numtests);
}
# 1: create an output directory for this test-run
}
}
-my @multi_worker;
-if (defined($multidpy)) {
- my $x = X11::XCB::Connection->new(display => $multidpy);
- if ($x->has_error) {
- die "Could not connect to multi-monitor display $multidpy\n";
- } else {
- push @multi_worker, worker($multidpy, $x, $outdir, \%options);
- }
-}
-
# Read previous timing information, if available. We will be able to roughly
# predict the test duration and schedule a good order for the tests.
my $timingsjson = StartXDummy::slurp('.last_run_timings.json');
my $num = @testfiles;
my $harness = TAP::Harness->new({ });
-my @single_monitor_tests = grep { m,^t/([0-9]+)-, && $1 < 500 } @testfiles;
-my @multi_monitor_tests = grep { m,^t/([0-9]+)-, && $1 >= 500 } @testfiles;
-
my $aggregator = TAP::Parser::Aggregator->new();
$aggregator->start();
-status_init(displays => [ @displays, $multidpy ], tests => $num);
+status_init(displays => \@displays, tests => $num);
my $single_cv = AE::cv;
-my $multi_cv = AE::cv;
# We start tests concurrently: For each display, one test gets started. Every
# test starts another test after completing.
for (@single_worker) {
$single_cv->begin;
- take_job($_, $single_cv, \@single_monitor_tests);
-}
-for (@multi_worker) {
- $multi_cv->begin;
- take_job($_, $multi_cv, \@multi_monitor_tests);
+ take_job($_, $single_cv, \@testfiles);
}
$single_cv->recv;
-$multi_cv->recv;
$aggregator->stop();
# If /proc/cpuinfo does not exist, we fall back to 2 cores.
$num_cores ||= 2;
- # If unset, we use num_cores * 2, plus two extra xdummys to combine to a
- # multi-monitor setup using Xdmx.
- $parallel ||= ($num_cores * 2) + 2;
+ # If unset, we use num_cores * 2.
+ $parallel ||= ($num_cores * 2);
# If we are running a small number of tests, don’t over-parallelize.
$parallel = $numtests if $numtests < $parallel;
- # Ensure we have at least 1 X-Server plus two X-Servers for multi-monitor
- # tests.
- $parallel = 3 if $parallel < 3;
-
# First get the last used display number, then increment it by one.
# Effectively falls back to 1 if no X server is running.
my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
wait_for_x(\@sockets_waiting);
- # Now combine the last two displays to a multi-monitor display using Xdmx
- my $first = pop @displays;
- my $second = pop @displays;
-
- # make sure this display isn’t in use yet
- $displaynum++ while -e ($x_socketpath . $displaynum);
- say 'starting xdmx on display :' . $displaynum;
-
- my $multidpy = ":$displaynum";
- my $socket = fork_xserver($displaynum, 'Xdmx', '+xinerama', '-xinput',
- 'local', '-display', $first, '-display', $second, '-ac', $multidpy);
- wait_for_x([ $socket ]);
-
- return \@displays, $multidpy;
+ return @displays;
}
1
if (exists($args{output})) {
my $i3 = i3(get_socket_path());
my $tree = $i3->get_tree->recv;
- my $output = first { $_->{name} eq "xinerama-$args{output}" }
+ my $output = first { $_->{name} eq "fake-$args{output}" }
@{$tree->{nodes}};
die "BUG: Could not find output $args{output}" unless defined($output);
# Get the focused workspace on that output and switch to it.
# Tests that the provided X-Server to the t/5??-*.t tests is actually providing
# multiple monitors.
#
-use i3test;
+use i3test i3_autostart => 0;
+
+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);
my $i3 = i3(get_socket_path());
my $tree = $i3->get_tree->recv;
my @outputs = map { $_->{name} } @{$tree->{nodes}};
-is_deeply(\@outputs, [ '__i3', 'xinerama-0', 'xinerama-1' ],
+is_deeply(\@outputs, [ '__i3', 'fake-0', 'fake-1' ],
'multi-monitor outputs ok');
+exit_gracefully($pid);
+
done_testing;
# ticket #596, bug present until up to commit
# 89dded044b4fffe78f9d70778748fabb7ac533e9.
#
-use i3test;
+use i3test i3_autostart => 0;
+
+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);
my $i3 = i3(get_socket_path());
verify_scratchpad_switch($first, $second);
+exit_gracefully($pid);
+
done_testing;
#
# Verifies the 'focus output' command works properly.
-use i3test;
+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);
+
my $tmp = fresh_workspace;
my $i3 = i3(get_socket_path());
return $output->{name};
}
-is(focused_output, 'xinerama-0', 'focus on first output');
+is(focused_output, 'fake-0', 'focus on first output');
cmd 'focus output right';
-is(focused_output, 'xinerama-1', 'focus on second output');
+is(focused_output, 'fake-1', 'focus on second output');
# focus should wrap when we focus to the right again.
cmd 'focus output right';
-is(focused_output, 'xinerama-0', 'focus on first output again');
+is(focused_output, 'fake-0', 'focus on first output again');
cmd 'focus output left';
-is(focused_output, 'xinerama-1', 'focus back on second output');
+is(focused_output, 'fake-1', 'focus back on second output');
cmd 'focus output left';
-is(focused_output, 'xinerama-0', 'focus on first output again');
+is(focused_output, 'fake-0', 'focus on first output again');
cmd 'focus output up';
-is(focused_output, 'xinerama-0', 'focus still on first output');
+is(focused_output, 'fake-0', 'focus still on first output');
cmd 'focus output down';
-is(focused_output, 'xinerama-0', 'focus still on first output');
+is(focused_output, 'fake-0', 'focus still on first output');
+
+cmd 'focus output fake-1';
+is(focused_output, 'fake-1', 'focus on second output');
-cmd 'focus output xinerama-1';
-is(focused_output, 'xinerama-1', 'focus on second output');
+cmd 'focus output fake-0';
+is(focused_output, 'fake-0', 'focus on first output');
-cmd 'focus output xinerama-0';
-is(focused_output, 'xinerama-0', 'focus on first output');
+exit_gracefully($pid);
done_testing;
# Tests whether 'workspace next_on_output' and the like work correctly.
#
use List::Util qw(first);
-use i3test;
+use i3test i3_autostart => 0;
+
+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);
################################################################################
# Setup workspaces so that they stay open (with an empty container).
cmd 'workspace prev_on_output';
is(focused_ws, '2', 'workspace 2 focused');
+exit_gracefully($pid);
+
done_testing;
# Tests whether the 'move workspace <ws> to [output] <output>' command works
#
use List::Util qw(first);
-use i3test;
+use i3test i3_autostart => 0;
# TODO:
# introduce 'move workspace 3 to output <output>' with synonym 'move workspace 3 to <output>'
+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);
+
################################################################################
# Setup workspaces so that they stay open (with an empty container).
################################################################################
is(focused_ws, '5', 'workspace 5 focused');
my ($x0, $x1) = workspaces_per_screen();
-ok('5' ~~ @$x0, 'workspace 5 on xinerama-0');
+ok('5' ~~ @$x0, 'workspace 5 on fake-0');
-cmd 'move workspace to output xinerama-1';
+cmd 'move workspace to output fake-1';
sub workspaces_per_screen {
my $i3 = i3(get_socket_path());
my $tree = $i3->get_tree->recv;
my @outputs = @{$tree->{nodes}};
- my $xinerama0 = first { $_->{name} eq 'xinerama-0' } @outputs;
- my $xinerama0_content = first { $_->{type} == 2 } @{$xinerama0->{nodes}};
+ my $fake0 = first { $_->{name} eq 'fake-0' } @outputs;
+ my $fake0_content = first { $_->{type} == 2 } @{$fake0->{nodes}};
- my $xinerama1 = first { $_->{name} eq 'xinerama-1' } @outputs;
- my $xinerama1_content = first { $_->{type} == 2 } @{$xinerama1->{nodes}};
+ my $fake1 = first { $_->{name} eq 'fake-1' } @outputs;
+ my $fake1_content = first { $_->{type} == 2 } @{$fake1->{nodes}};
- my @xinerama0_workspaces = map { $_->{name} } @{$xinerama0_content->{nodes}};
- my @xinerama1_workspaces = map { $_->{name} } @{$xinerama1_content->{nodes}};
+ my @fake0_workspaces = map { $_->{name} } @{$fake0_content->{nodes}};
+ my @fake1_workspaces = map { $_->{name} } @{$fake1_content->{nodes}};
- return \@xinerama0_workspaces, \@xinerama1_workspaces;
+ return \@fake0_workspaces, \@fake1_workspaces;
}
($x0, $x1) = workspaces_per_screen();
-ok('5' ~~ @$x1, 'workspace 5 now on xinerama-1');
+ok('5' ~~ @$x1, 'workspace 5 now on fake-1');
################################################################################
# Verify that a new workspace will be created when moving the last workspace.
################################################################################
-is_deeply($x0, [ '1' ], 'only workspace 1 remaining on xinerama-0');
+is_deeply($x0, [ '1' ], 'only workspace 1 remaining on fake-0');
cmd 'workspace 1';
-cmd 'move workspace to output xinerama-1';
+cmd 'move workspace to output fake-1';
($x0, $x1) = workspaces_per_screen();
-ok('1' ~~ @$x1, 'workspace 1 now on xinerama-1');
-is_deeply($x0, [ '3' ], 'workspace 2 created on xinerama-0');
+ok('1' ~~ @$x1, 'workspace 1 now on fake-1');
+is_deeply($x0, [ '3' ], 'workspace 2 created on fake-0');
################################################################################
# Verify that 'move workspace to output <direction>' works
cmd 'move workspace to output left';
($x0, $x1) = workspaces_per_screen();
-ok('5' ~~ @$x0, 'workspace 5 back on xinerama-0');
+ok('5' ~~ @$x0, 'workspace 5 back on fake-0');
################################################################################
# Verify that coordinates of floating windows are fixed correctly when moving a
is($old_rect->{width}, $new_rect->{width}, 'width unchanged');
is($old_rect->{height}, $new_rect->{height}, 'height unchanged');
+exit_gracefully($pid);
+
done_testing;